传送门
最长递增子序列
题意:计算最长不降子序列长度s与数量,以及多次使用首末元素之后最多取出多少长度为s的最长不降子序列。
I think
第一问DP求出f[i],表示以第i位为首位的最长不降子序列长度len,二三问网络流求解。增设源汇点S T,S向f[i]==len的点i连边,f[i]==1的点向T连边,满足i< j&&a[i]< =a[j]&&f[i]==f[j]+1的点对 < i,j > 连边,边的容量均为1,,跑最大流即为答案。第三问将S向1,N向T的连边容量更改为Inf,再次跑网络流得解。
Code
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int sm = 500+5;
const int sn = 125750;
const int Inf = 0x3f3f3f3f;
int x,y,Flw;
int N,M,S,T,tot=1,len;
int a[sm],f[sm],lev[sm],cur[sm];
int to[sn],hd[sm],c[sn],nxt[sn],_c[sn];
int Max(int x,int y) { return x>y?x:y; }
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v,int w) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=_c[tot]=w;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=_c[tot]=0;
}
bool Bfs() {
for(int i=1;i<=T;++i) lev[i]=0;
int t; queue<int>q;
q.push(S),lev[S]=1;
while(!q.empty()) {
t=q.front(),q.pop();
for(int i=hd[t];i;i=nxt[i])
if(c[i]>0&&!lev[to[i]]) {
lev[to[i]]=lev[t]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
return 0;
}
int Dfs(int x,int mx) {
if(x==T||!mx) return mx;
int f;
for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
cur[x]=i;
if(c[i]&&lev[to[i]]==lev[x]+1)
if(f=Dfs(to[i],Min(mx,c[i])))
return c[i]-=f,c[i^1]+=f,f;
}
return 0;
}
void Dinic() {
int f; Flw=0;
while(Bfs()) {
for(int i=1;i<=T;++i) cur[i]=0;
while(f=Dfs(S,Inf)) Flw+=f;
}
}
int main() {
scanf("%d",&N);
for(int i=1;i<=N;++i)
scanf("%d",&a[i]);
for(int i=N;i>=1;--i) {
f[i]=1;
for(int j=i+1;j<=N;++j)
if(a[j]>=a[i])
f[i]=Max(f[i],f[j]+1);
len=Max(len,f[i]);
}
S=N+1,T=S+1;
for(int i=1;i<=N;++i) {
if(f[i]==len) {
if(i==1) x=tot+1;
Add(S,i,1);
}
if(f[i]==1) {
if(i==N) y=tot+1;
Add(i,T,1);
}
}
for(int i=1;i<N;++i)
for(int j=i+1;j<=N;++j)
if(a[j]>=a[i]&&f[j]+1==f[i])
Add(i,j,1);
Dinic();
printf("%d\n%d\n",len,Flw);
if(!x&&!y) printf("%d\n",Flw);
else {
memcpy(c,_c,sizeof(c));
if(x) c[x]=Inf;
if(y) c[y]=Inf;
Dinic();
printf("%d\n",Flw);
}
return 0;
}