• [网络流24题]最长不下降子序列问题


    题目:洛谷P2766、codevs1906。

    题目大意:给你一个数列,要你回答:

    1.它的最长不下降子序列;

    2.在这个数列中,能取出多少种元素不重复(下标不同即可)的最长不下降子序列;

    3.现在规定第一个元素和最后一个元素可以重复利用,问第二问。

    解题思路:codevs题面有误,应该是不下降而不是递增。

    第一问DP,$O(n^2)$求出f(f[i]表示以第i个元素结尾的最长不下降子序列长度)。

    第二、三问都是网络流。

    首先对每个元素拆成入点和出点(设分别为i和i+n),然后

    对于f[i]=1的i,连接0(超级源点)和i(i的入点),容量1。

    对于f[i]=n的i,连接2n(n的出点)和2n+1(超级汇点),容量为1。

    然后,对于每一对i<j,若a[i]<=a[j]且f[i]+1=f[j],则连接i+n(i的出点)和j(j的入点),容量为1。

    容量为1是因为每个元素只能使用一次。

    跑网络流即是第二问的答案。

    第三问呢?

    由于两个点没有限制,所以把相关边的容量设为inf即可。

    所以只要加上0到1,1到n+1的边,和n到2n,2n到2n+1的边(这两条仅当f[n]的值是最长不下降子序列长度时才添加),容量都为inf,

    然后在残量网络中继续跑,最后和原来的答案加起来即可。(当然也可以重新构图)。

    注意:当原数列严格递减时(即DP答案为1时),答案应该是1,n,n。此时网络流可能跑出问题,需要加特判。

    C++ Code:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3fffffff
    std::queue<int>q;
    int a[505],f[505],n,head[1007],cnt=0,level[1007],iter[1007];
    struct edge{
    	int to,cap,nxt,rev;
    }e[1005*1005*2];
    inline void addedge(int u,int v,int flow){
    	++cnt;
    	e[cnt]=(edge){v,flow,head[u],cnt+1};
    	head[u]=cnt;
    	++cnt;
    	e[cnt]=(edge){u,0,head[v],cnt-1};
    	head[v]=cnt;
    }
    void bfs(int s){
    	level[s]=1;
    	q.push(s);
    	while(!q.empty()){
    		int u=q.front();
    		q.pop();
    		for(int i=head[u];i!=-1;i=e[i].nxt)
    		if(level[e[i].to]<0&&e[i].cap){
    			level[e[i].to]=level[u]+1;
    			q.push(e[i].to);
    		}
    	}
    }
    int dfs(int u,int t,int f){
    	if(u==t)return f;
    	for(int& i=iter[u];i!=-1;i=e[i].nxt)
    	if(level[e[i].to]>level[u]&&e[i].cap){
    		int d=dfs(e[i].to,t,f>e[i].cap?e[i].cap:f);
    		if(d){
    			e[i].cap-=d;
    			e[e[i].rev].cap+=d;
    			return d;
    		}
    	}
    	return 0;
    }
    int maxflow(int s,int t){
    	for(int flow=0;;){
    		memset(level,-1,sizeof level);
    		bfs(s);
    		if(level[t]==-1)return flow;
    		memcpy(iter,head,sizeof iter);
    		int f=0;
    		while(f=dfs(s,t,inf))flow+=f;
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    	memset(f,0,sizeof f);
    	f[1]=1;
    	int ans1=1;
    	for(int i=2,j;i<=n;++i)
    	for(j=f[i]=1;j<i;++j)
    	if(a[j]<=a[i]&&f[j]+1>f[i]){
    		f[i]=f[j]+1;
    		if(ans1<f[i])ans1=f[i];
    	}
    	if(ans1==1)return!printf("%d
    %d
    %d
    ",1,n,n);
    	printf("%d
    ",ans1);
    	memset(head,-1,sizeof head);
    	for(int i=1;i<=n;++i){
    		addedge(i,i+n,1);
    		if(f[i]==ans1)addedge(i+n,n<<1|1,1);else{
    			if(f[i]==1)addedge(0,i,1);
    			for(int j=i+1;j<=n;++j)
    			if(a[i]<=a[j]&&f[i]+1==f[j])addedge(i+n,j,1);
    		}
    	}
    	int ans2;
    	printf("%d
    ",ans2=maxflow(0,n<<1|1));
    	addedge(0,1,inf);
    	if(f[n]==ans1)addedge(n<<1,n<<1|1,inf),addedge(n,n<<1,inf);
    	addedge(1,n+1,inf);
    	printf("%d
    ",ans2+maxflow(0,n<<1|1));
    	return 0;
    }
    
  • 相关阅读:
    [HIHO1223]不等式(离散化,枚举)
    [NYIST15]括号匹配(二)(区间dp)
    [HIHO1328]逃离迷宫(bfs,位压)
    [Topcoder]AvoidRoads(dp,hash)
    [POJ1159]Palindrome(dp,滚动数组)
    [Topcoder]ZigZag(dp)
    [NYIST32]组合数(状压,枚举,暴力)
    [NYIST737]石子合并(一)(区间dp)
    [HIHO1322]树结构判定(并查集)
    [HIHO1143]骨牌覆盖问题·一(矩阵快速幂,递推)
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/8231029.html
Copyright © 2020-2023  润新知