zoukankan      html  css  js  c++  java
  • codeforces568E.Longest Increasing Subsequence

    传送门: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
    ",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
    */


  • 相关阅读:
    PAIRING WORKFLOW MANAGER 1.0 WITH SHAREPOINT 2013
    Education resources from Microsoft
    upgrade to sql server 2012
    ULSViewer sharepoint 2013 log viewer
    Top 10 Most Valuable Microsoft SharePoint 2010 Books
    讨论 Setsockopt选项
    使用 Alchemy 技术编译 C 语言程序为 Flex 可调用的 SWC
    Nagle's algorithm
    Nagle算法 TCP_NODELAY和TCP_CORK
    Design issues Sending small data segments over TCP with Winsock
  • 原文地址:https://www.cnblogs.com/thythy/p/5493515.html
Copyright © 2011-2022 走看看