给定正整数序列x1,..... , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1.....xn 。
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
4
3 6 2 5
2
2
3
注意:虽然题目是最长递增子序列,但是code vs上的测试数据是按最长不下降子序列来的
题解:
1. dp求最长不下降子序列,不用多说
2.建图(看程序,比较好理解),跑最大流
3.与2相似,因为1,n可以多次使用,所以只需加大源点到1的容量和n到汇点的容量即可
另外此题还是需要拆点,拆成的两个点之间连一条容量为1的边,因为只有这样才能控制每个点之用一次
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int point[2100],next[2000003],v[2000003],remain[2000003]; int deep[2100],cur[2100],num[2100],n,m,tot,ans,laste[2100]; int a[2100],f[2100]; const int inf=1e9; void add(int x,int y,int z) { tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; } int adde(int s,int t) { int now=t,minn=inf; while (now!=s) { minn=min(minn,remain[laste[now]]); now=v[laste[now]^1]; } now=t; while (now!=s) { remain[laste[now]]-=minn; remain[laste[now]^1]+=minn; now=v[laste[now]^1]; } return minn; } void bfs(int s,int t) { for (int i=s;i<=t;i++) deep[i]=t+2; deep[t]=0; queue<int> p; p.push(t); while (!p.empty()) { int now=p.front(); p.pop(); for (int i=point[now];i!=-1;i=next[i]) if (deep[v[i]]==t+2&&remain[i^1]) { deep[v[i]]=deep[now]+1; p.push(v[i]); } } } int isap(int s,int t) { int flow=0,now=s; bfs(s,t); memset(num,0,sizeof(num)); for (int i=s;i<=t;i++) num[deep[i]]++; for (int i=s;i<=t;i++) cur[i]=point[i]; while (deep[s]<=t) { if (now==t) { flow+=adde(s,t); now=s; } bool hasfind=false; for (int i=cur[now];i!=-1;i=next[i]) { if (deep[v[i]]+1==deep[now]&&remain[i]) { hasfind=true; cur[now]=i; laste[v[i]]=i; now=v[i]; break; } } if (!hasfind) { int minn=t; for (int i=point[now];i!=-1;i=next[i]) if (remain[i]) minn=min(minn,deep[v[i]]+1); if (!--num[deep[now]]) break; deep[now]=minn; num[minn]++; cur[now]=point[now]; if (now!=s) now=v[laste[now]^1]; } } return flow; } void solve1() { for (int i=1;i<=n;i++) { if (f[i]==1) add(0,i,1); if (f[i]==ans) add(i+n,2*n+1,1); add(i,i+n,1); } for (int i=2;i<=n;i++) { for (int j=1;j<i;j++) if (f[i]==f[j]+1&&a[i]>=a[j]) add(j+n,i,1); } if (ans==1) printf("%d\n",n); else printf("%d\n",isap(0,2*n+1)); } void solve2() { tot=-1; memset(next,-1,sizeof(next)); memset(point,-1,sizeof(point)); for (int i=1;i<=n;i++) { int v=1; if (i==1||i==n) v=inf; if (f[i]==1) add(0,i,v); if (f[i]==ans) add(i+n,2*n+1,v); add(i,i+n,v); } for (int i=2;i<=n;i++) { for (int j=1;j<i;j++) if (f[i]==f[j]+1&&a[i]>=a[j]) add(j+n,i,1); } if (ans==1) printf("%d\n",n); else printf("%d\n",isap(0,2*n+1)); } int main() { tot=-1; memset(next,-1,sizeof(next)); memset(point,-1,sizeof(point)); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); f[1]=1; for (int i=2;i<=n;i++) { for (int j=1;j<i;j++) if (f[i]<f[j]&&a[j]<=a[i]) f[i]=f[j]; f[i]++; } ans=0; for (int i=1;i<=n;i++) ans=max(ans,f[i]); printf("%d\n",ans); solve1(); solve2(); }