您的位置:首页 > 产品设计 > UI/UE

codeforces568E.Longest Increasing Subsequence

2015-10-23 15:10 465 查看
传送门:http://codeforces.com/problemset/problem/568/E

思路:首先没有空位,我们是记录一个low数组表示长度为i的上升子序列的最小结尾。

对于一个末尾新的数x,我们只要二分出一个位置low[i]<x<=low[i+1],即可更新low[i+1]的答案

现在有空位和m个数可用,每个数只能用一次。

其实每个数只能用一次这个条件并没有什么用,因为这是严格的LIS,用两次也没有用

所以可以先去重。

然后O(n+m)地枚举去更新low数组,因为空位数k<=1000,所以这是可以过的。

至于输出方案,记录

pre[i]i结尾的最长LIS的上一个位置(空位为-1)

f[i]i结尾LIS最长长度

last[len] 长度为len的LIS最小结尾位置 (空位为-1)

倒序求方案

设当前位置为x

如果pre[x]!=-1,即上一位不是空位,直接x=pre[x]

否则枚举前面不是空位的一个位置y,填充好f[y]-f[x]个空位再令x=y即可

什么样的y可能在LIS中呢?

只要满足f[x]-f[y]=min(gap(x,y),num(x,y))即可

gap(l,r)为(l,r)间的空位数num(x,y)为m个数中有多少个可以填在a[x],a[y]之间

填完LIS后,无关紧要的空位随便填没有用过的数即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=100010,inf=1e9;
using namespace std;
int n,m,a[maxn],b[maxn],low[maxn],pre[maxn],f[maxn],last[maxn],gap[maxn];
//low[len]长度为len的LIS最小结尾
//pre[i]i结尾的最长LIS的上一个位置,输方案要用
//f[i]i结尾LIS最长长度
//last[len] 长度为len的LIS最小结尾位置
void work(){
memset(low,63,sizeof(low));
pre[1]=0,f[1]=1,last[1]=1,low[1]=0;
for (int k=2;k<=n;k++){
if (a[k]!=-1){//有数
int l=1,r=n,mid=(l+r)>>1;
while (l<=r){
if (low[mid]<a[k]) l=mid+1;
else r=mid-1;
mid=(l+r)>>1;
}
pre[k]=last[l-1],last[l]=k;
f[k]=l,low[l]=a[k];
}
else{//gap
pre[k]=-1,f[k]=-1;
for (int i=m,j=n;i>=1;i--){//从大到小枚举可填的数
while (low[j]>=b[i]) j--;//low有单调性,所以不用二分。
low[j+1]=b[i],last[j+1]=-1;
}
}
}
//	for (int i=n;i;i--)if (low[i]>0&&low[i]!=1061109567) {printf("%d %d\n",i,low[i]);break;}
}

void print(){//倒序求方案
int l,r,rest=m;//l,r表示b[l]-b[r]的数可以填在a[x]和a[y]之间 ,rest表示可用的数还有哪些
for (int x=n;x>=1;) if (a[x]!=-1){
if (pre[x]>=0){x=pre[x];continue;}//LIS上一位有数,跳到上一位
r=lower_bound(b+1,b+1+rest,a[x])-b-1;
for (int y=x-1;y;y--)//否则枚举上一个不是空位的位置
if (a[y]!=-1&&a[y]<a[x]){
l=upper_bound(b+1,b+1+rest,a[y])-b;
if (f[x]-f[y]-1==min(r-l+1,gap[x]-gap[y])){//如果满足,位置y就在一种LIS中
for (int i=y,j=l;i<=x&&j<=r;i++)
if (a[i]==-1) a[i]=b[j],b[j]=-1,j++;//填充y到x这段区间
x=y,rest=l-1;break;//继续向前做
}
}
}
for (int i=1,j=1;i<=n;i++){
if (a[i]==-1){
for (;b[j]==-1;j++);
a[i]=b[j],b[j++]=-1;
}
}
for (int i=2;i<n;i++) printf("%d ",a[i]);puts("");
}

int main(){
scanf("%d",&n),gap[1]=gap[0]=0;
for (int i=2;i<=n+1;i++) scanf("%d",&a[i]),gap[i]=gap[i-1]+(a[i]==-1);
a[1]=0,a[n+2]=inf,gap[n+2]=gap[n+1],n+=2;
scanf("%d",&m);for (int i=1;i<=m;i++) scanf("%d",&b[i]);
sort(b+1,b+1+m),m=unique(b+1,b+1+m)-b-1;//LIS是严格上升的,所以每个数最多用一次
work(),print();
return 0;
}
/*
100
-1 2 3 74 90 39 37 18 23 -1 5 -1 56 88 99 49 72 11 19 6 81 24 8 23 64 -1 100 77 61 87 23 -1 20 15 -1 55 25 40 4 25 73 85 87 72 5 98 46 49 -1 67 81 58 3 -1 22 14 -1 -1 92 -1 78 53 64 23 84 10 -1 54 83 55 24 -1 79 23 92 41 -1 -1 93 -1 59 90 64 93 95 22 -1 67 -1 33 41 84 37 73 -1 -1 18 49 50 58
20
57 23 22 93 77 19 85 32 79 94 20 59 78 35 16 92 33 94 42 11
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: