网络流24题 P2766 最长不下降子序列问题
题目描述
«问题描述:
给定正整数序列x1,...,xn 。
(1)计算其最长不下降子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
输入格式
第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。
输出格式
第1 行是最长不下降子序列的长度s。第2行是可取出的长度为s 的不下降子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的不下降子序列个数。
输入输出样例
输入
4
3 6 2 5
输出
2
2
3
说明/提示
n≤500
解析
(1) n^2的dp 求出S (2)从源点向dp[i]=1的点连一条容量为1的边,dp[i]=S的点向汇点连一条容量为1的边。 因为每个点只能用一次,所以我们还要把一个点拆成两个i 和 i+n,在它们之间连一条容量为1的边。 之后n^2枚举每两个点,若两个点i,j满足a[j]<=a[i]且dp[i]=dp[j]+1,则在i+n,j之间连一条容量为1的边。 建好图后跑一边最大流,就是第二问的答案。 (3)将源点向dp[i]=1的点连的边的容量改为inf,dp[i]=1的点与拆出来的点边权也要设为inf。同理汇点也一样。
代码
// luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; const int maxn=1e3+20,mod=1e9+7,inf=0x3f3f3f3f; typedef long long ll; struct edge { int from,to,c,f; edge(int u,int v,int c,int f):from(u),to(v),c(c),f(f) {} }; int n,m; vector<edge> edges; vector<int> g[maxn]; int d[maxn];//从起点到i的距离 int cur[maxn];//当前弧下标 void init(int N) { for(int i=0; i<=N; i++) g[i].clear(); edges.clear(); } void addedge(int from,int to,int c) //加边 支持重边 { edges.push_back(edge(from,to,c,0)); edges.push_back(edge(to,from,0,0)); int siz=edges.size(); g[from].push_back(siz-2); g[to].push_back(siz-1); } int bfs(int s,int t) //构造一次层次图 { memset(d,-1,sizeof(d)); queue<int> q; q.push(s); d[s]=0; while(!q.empty()) { int x=q.front();q.pop(); for(int i=0;i<g[x].size();i++) { edge &e=edges[g[x][i]]; if(d[e.to]<0&&e.f<e.c) //d[e.to]=-1表示没访问过 { d[e.to]=d[x]+1; q.push(e.to); } } } return d[t]; } int dfs(int x,int a,int t) // a表示x点能接收的量 { if(x==t||a==0)return a; int flow=0,f;//flow总的增量 f一条增广路的增量 for(int &i=cur[x];i<g[x].size();i++)//cur[i] &引用修改其值 从上次考虑的弧 { edge &e=edges[g[x][i]]; if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.c-e.f),t))>0) //按照层次图增广 满足容量限制 { e.f+=f; edges[g[x][i]^1].f-=f; //修改流量 flow+=f; a-=f; if(a==0) break; } } return flow; } int maxflow(int s,int t) { int flow=0; while(bfs(s,t)!=-1) //等于-1代表构造层次图失败 结束 { memset(cur,0,sizeof(cur)); flow+=dfs(s,inf,t); } return flow; } int a[1005],dp[1005]; int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); int maxdp=0; for(int i=1;i<=n;i++){ dp[i]=1; for(int j=1;j<i;j++){ if(a[j]<=a[i]) dp[i]=max(dp[i],dp[j]+1); } maxdp=max(maxdp,dp[i]); } printf("%d\n",maxdp); if(maxdp==1){ printf("%d\n%d\n",n,n); continue; } init(2*n+1); for(int i=1;i<=n;i++){ if(dp[i]==1) addedge(0,i,1); if(dp[i]==maxdp) addedge(i+n,2*n+1,1); addedge(i,i+n,1); for(int j=1;j<i;j++){ if(a[j]<=a[i]&&dp[i]==dp[j]+1) addedge(j+n,i,1); } } printf("%d\n",maxflow(0,2*n+1)); init(2*n+1); for(int i=1;i<=n;i++){ if(dp[i]==1)addedge(0,i,inf); if(dp[i]==maxdp) addedge(i+n,2*n+1,inf); if(dp[i]==1||dp[i]==maxdp)addedge(i,i+n,inf); else addedge(i,i+n,1); for(int j=1;j<i;j++){ if(a[j]<=a[i]&&dp[i]==dp[j]+1) addedge(j+n,i,1); } } printf("%d\n",maxflow(0,2*n+1)); } }