• P2766 最长不下降子序列问题 题解(网络流)


    题目链接

    最长不下降子序列问题

    解题思路

    分成三小问解决。
    第一小问,求(LIS),因为(n<=500),直接(O(N^2))暴力求解即可。
    第二三小问,建立模型用网络流求解。
    对于第二小问
    ((1))首先,因为每个点只能使用一次,考虑拆点,把每一个点拆成(i,n+i)两个点,从(i)连向(n+i)一条长度为(1)的有向边。
    ((2))其次,因为流向是从S经集合E到T,其中任意集合E中元素(i)需要满足的条件是(i)位于LIS上,故:
    ①出边从(lis[i]=k)的点流向T
    ②所有的能构成E的边都需要加入,也就是任意(i,j)满足(i<j,a[i]<a[j],lis[i]=lis[j]-1)都从(i)(j)连边。
    跑一遍最大流即可。
    第三小问唯一的不同就是(S)流向(1)(1)流向(1+n)(n)流向(n+n)(n+n)流向(T)四条路长度变成(inf)而已,改后再跑一遍最大流即可。

    AC代码

    #include<stdio.h>
    #include<string.h>
    int lis[1020],a[1020],len;//lis
    int s,t,inf=0x3fffffff;//ek
    struct Edge{
    	int end,length,near;
    }edge[50020];
    int head[1020],cnt=2,vis[1020];
    void addedge(int begin,int end,int length){
    	edge[cnt].end=end;
    	edge[cnt].length=length;
    	edge[cnt].near=head[begin];
    	head[begin]=cnt;
    	cnt++;
    }
    struct Pre{
    	int pre,ednum;
    }pre[1020];
    int bfs(){	
    	memset(vis,0,sizeof(vis));
    	int queue[50010]={0},headq=0,tail=0,i;
    	queue[tail++]=s;
    	vis[s]=1;
    	while(headq<tail){
    		int v=queue[headq];
    		for(i=head[v];i;i=edge[i].near){
    			int p=edge[i].end;
    			if(vis[p]||!edge[i].length)continue;
    			vis[p]=1;
    			pre[p].pre=v;
    			pre[p].ednum=i;
    			queue[tail++]=p;
    			if(p==t)return 1;
    		}
    		headq++;
    	}
    	return 0;
    }
    int ek(){
    	int min,ans=0,i;
    	while(bfs()){
    		min=inf;
    		for(i=t;i!=s;i=pre[i].pre){
    			int en=pre[i].ednum;
    			if(min>edge[en].length)min=edge[en].length;
    		}
    		for(i=t;i!=s;i=pre[i].pre){
    			int en=pre[i].ednum;
    			edge[en].length-=min;
    			edge[en^1].length+=min;
    		}
    		ans+=min;
    	}
    	return ans;
    }
    int main(){
    	int i,j,n;
    	scanf("%d",&n);
    	s=2*n+3;
    	t=2*n+4;
    	for(i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		lis[i]=1;
    	}
    	//lis n<=500 O(N2) 
    	for(i=1;i<=n;i++)
    		for(j=1;j<i;j++)
    			if(a[i]>=a[j]&&lis[i]<lis[j]+1)lis[i]=lis[j]+1;
    	for(i=1;i<=n;i++)if(lis[i]>len)len=lis[i];
    	printf("%d
    ",len);
    	//ek1 建图求最大流 
    	for(i=1;i<=n;i++){
    		addedge(i,i+n,1);//拆点 
    		addedge(i+n,i,0);
    		if(lis[i]==1){
    			addedge(s,i,1);
    			addedge(i,s,0);
    		}
    		if(lis[i]==len){
    			addedge(i+n,t,1);
    			addedge(t,i+n,0);
    		}
    	}
    	for(i=1;i<=n;i++){
    		for(j=1;j<i;j++){
    			if(lis[i]==lis[j]+1&&a[i]>=a[j]){
    				addedge(j+n,i,1);	addedge(i,j+n,0);
    			}
    		}
    	}
    	printf("%d
    ",ek());
    	//ek2
    	memset(pre,0,sizeof(pre));
    	memset(edge,0,sizeof(edge));
    	memset(head,0,sizeof(head));
    	cnt=2;
    	for(i=1;i<=n;i++){
    		addedge(i,i+n,1);//拆点 
    		addedge(i+n,i,0);
    		if(lis[i]==1){
    			addedge(s,i,1);
    			addedge(i,s,0);
    		}
    		if(lis[i]==len){
    			addedge(i+n,t,1);
    			addedge(t,i+n,0);
    		}
    	}
    	for(i=1;i<=n;i++){
    		for(j=1;j<i;j++){
    			if(lis[i]==lis[j]+1&&a[i]>=a[j]){
    				addedge(j+n,i,1);	addedge(i,j+n,0);
    			}
    		}
    	}
    	addedge(1,1+n,inf);	addedge(1+n,1,0);
    	addedge(s,1,inf); 	addedge(1,s,0); 
    	if(lis[n]==len){
    		addedge(n,n+n,inf);		addedge(n+n,n,0);
    		addedge(n+n,t,inf);		addedge(t,n+n,0);
    	}
    	printf("%d
    ",ek());
    	return 0;
    }
    
  • 相关阅读:
    Linux下守护进程的编程实现(转)
    gcc 中-I、 -L 与-l选项的作用
    va_list 、va_start、 va_arg、 va_end 使用说明
    sizeof('a')在C与C++中的区别
    Qt主要类简介
    linux命令
    linux文件编程
    linux一些基本命令
    Linux主机SSH免密设置解析
    javaweb添加拦截器
  • 原文地址:https://www.cnblogs.com/Potassium/p/10012545.html
Copyright © 2020-2023  润新知