时空限制1000ms / 128MB
题目描述
«问题描述:
给定正整数序列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 的不下降子序列个数。
输入输出样例
说明
n≤500
题目链接:https://www.luogu.org/problemnew/show/P2766
最大流。第一问可用dp解决。对于第二问,设最长不下降子序列的长度为k,问题难在怎么满足从源点到汇点的一条增广路恰好选取k个不下降的数。我们可从第一问的dp数组上做文章,dp[i]代表以第i个数结尾的最长不下降子序列的长度是多少。对每一个数ai,对于它后面的数aj,若ai<=aj且dp[i]+1=dp[j],则从i向j连一条边。最后源点与dp值为1的点连边,汇点与dp值为k的点连边。这样就保证每一条从源点到汇点的可行流,都恰好经过k个不下降的数。还有一点要说明的是,对于每一个点,都要拆点来限制每个点只能被选一次。第三问直接修改第二问中对点的限制就好了。
#include<bits/stdc++.h> #define INF LLONG_MAX/2 #define N 1005 using namespace std; struct ss { int v,next; long long flow; }; int head[N],now_edge=0,S,T; ss edg[N*N]; void init() { now_edge=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,long long flow) { edg[now_edge]=(ss){v,head[u],flow}; head[u]=now_edge++; edg[now_edge]=(ss){u,head[v],0}; head[v]=now_edge++; } int dis[N]; int bfs() { memset(dis,0,sizeof(dis)); queue<int>q; q.push(S); dis[S]=1; while(!q.empty()) { int now=q.front(); q.pop(); for(int i=head[now];i!=-1;i=edg[i].next) { ss &e=edg[i]; if(e.flow>0&&dis[e.v]==0) { dis[e.v]=dis[now]+1; q.push(e.v); } } } if(dis[T]==0)return 0; return 1; } int current[N]; long long dfs(int x,long long maxflow) { if(x==T)return maxflow; for(int i=current[x];i!=-1;i=edg[i].next) { current[x]=i; ss &e=edg[i]; if(e.flow>0&&dis[e.v]==dis[x]+1) { long long flow=dfs(e.v,min(maxflow,e.flow)); if(flow!=0) { e.flow-=flow; edg[i^1].flow+=flow; return flow; } } } return 0; } long long dinic() { long long ans=0,flow; while(bfs()) { for(int i=0;i<N;i++)current[i]=head[i]; while(flow=dfs(S,INF))ans+=flow; } return ans; } int main() { int n; int arr[N]; int dp[N]={0}; int ans1=1,ans2=0; init(); scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&arr[i]); dp[1]=1; for(int i=2;i<=n;i++) { int Max=0; for(int j=1;j<i;j++)if(arr[j]<=arr[i])Max=max(Max,dp[j]); dp[i]=Max+1; ans1=max(ans1,dp[i]); } printf("%d ",ans1); S=n*2+1; T=S+1; for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) if(dp[j]==dp[i]+1&&arr[j]>=arr[i])addedge(2*i,2*j-1,1); addedge(i*2-1,i*2,1); if(dp[i]==1)addedge(S,2*i-1,1); if(dp[i]==ans1)addedge(2*i,T,1); } ans2=dinic(); printf("%d ",ans2); addedge(1,2,INF); addedge(2*n-1,2*n,INF); addedge(S,1,INF); if(dp[n]==ans1)addedge(2*n,T,INF); ans2+=(int)dinic(); printf("%d ",ans2); return 0; }