题目:洛谷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; }