求最长不下降子序列,每个数只能取一次时最多能拼出多少最长不下降子序列,第
1
1
1个数和第
n
n
n个数能用无限次其它的只能用一次时最多能拼出多少最长不下降子序列。
第一问就略过吧。
因为每个数只能用一次,所以可以把点拆开,设原点为
i
i
i,那么现在拆出
i
a
i_a
ia和
i
b
i_b
ib。
考虑到最长上升子序列长度固定,我们又算出了
f
[
i
]
f[i]
f[i],所以我们可以根据
f
[
i
]
f[i]
f[i]来分层。
那么建边就是这样的:
- 源点和所有 f [ i ] = 1 f[i]=1 f[i]=1的 i a i_a ia连边,流量为 1 1 1
- 所有 i a i_a ia和 i b i_b ib连边,流量为 1 1 1
- 所有 f [ i ] = m a x f[i]=max f[i]=max的 i b i_b ib和汇点连边,流量为 1 1 1
这样就可以了。
第三问只让
1
1
1和
n
n
n无限使用,考虑到上面的图控制使用次数的边就是这个点拆出的两个点之间的流量,所以我们把
1
a
1_a
1a和
1
b
1_b
1b之间的流量改为无限,
n
a
n_a
na和
n
b
n_b
nb之间的流量改为无限,
S
S
S和
1
a
1_a
1a之间的流量改为无限,如果
f
[
n
]
=
m
a
x
f[n]=max
f[n]=max,那么就把
n
b
n_b
nb和
T
T
T之间的流量改为无限。
c
o
d
e
:
code:
code:
#include <bits/stdc++.h>
int n;
int max;
int S=1,T=2;
int a[1000000];
int f[1000000];
int head[3000],tot;
int cur[3000];
int deep[3000];
struct edge{
int to;
int nxt;
int flow;
}e[1000000];
std::queue<int>q;
void add(int x,int y,int flow){
e[++tot]={y,head[x],flow};
head[x]=tot;
e[++tot]={x,head[y],0};
head[y]=tot;
}
bool bfs(){
memset(deep,0,sizeof deep);
deep[S]=1;
q.push(S);
while(!q.empty()){
int X=q.front();
q.pop();
for(int i=head[X];i;i=e[i].nxt){
int y=e[i].to;
if(!deep[y]&&e[i].flow){
deep[y]=deep[X]+1;
q.push(y);
}
}
}
return deep[T];
}
int dfs(int x,int flow){
if(x==T||!flow)
return flow;
int Flow=0;
for(int &i=cur[x];i;i=e[i].nxt){
int y=e[i].to;
if(e[i].flow&&deep[y]==deep[x]+1){
if(int w=dfs(y,std::min(flow,e[i].flow))){
e[i].flow-=w;
e[i^1].flow+=w;
Flow+=w;
flow-=w;
if(!flow)
break;
}
}
}
return Flow;
}
void dinic(){
int maxflow=0;
while(bfs()){
memcpy(cur,head,sizeof head);
while(int w=dfs(S,0x3f3f3f3f))
maxflow+=w;
}
printf("%d\n",maxflow);
add(S,3,0x3f3f3f3f);
add(3,3+n,0x3f3f3f3f);
if(f[n]==max){
add(2+2*n,T,0x3f3f3f3f);
add(2+n,2+2*n,0x3f3f3f3f);
}
while(bfs()){
memcpy(cur,head,sizeof head);
while(int w=dfs(S,0x3f3f3f3f))
maxflow+=w;
}
printf("%d\n",maxflow);
}
main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
f[1]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j)
if(a[i]>=a[j])
f[i]=std::max(f[i],f[j]+1);
max=std::max(max,f[i]);
}
for(int i=1;i<=n;++i)
if(f[i]==1)
add(S,2+i,1);
for(int i=1;i<=n;++i){
add(2+i,2+n+i,1);
for(int j=1;j<i;++j){
if(a[j]<=a[i]&&f[j]+1==f[i])
add(2+j+n,2+i,1);
}
}
for(int i=1;i<=n;++i)
if(f[i]==max)
add(2+n+i,T,1);
printf("%d\n",max);
dinic();
return 0;
}