P2766 最長不下降子序列問題 題解(網路流)
阿新 • • 發佈:2018-11-24
題目連結
解題思路
分成三小問解決。
第一小問,求\(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\)
跑一遍最大流即可。
第三小問唯一的不同就是\(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\n",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\n",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\n",ek()); return 0; }