题目传送门
题目大意: 三个问:1、最长不下降子序列长度(设为 k k k),2、每个元素只能用一次,最多能取出多少个长度为 k k k 的不下降子序列,3、第 1 1 1 个和第 n n n 个元素可以重复使用,其它与第 2 2 2 问一样。
第一问但凡有点 d p dp dp 基础都能做吧qwq
第二问的话直接建图,设 f [ i ] f[i] f[i] 表示以第 i i i 个元素结尾的最长不下降子序列长度,那么让所有 f [ i ] = 1 f[i]=1 f[i]=1 的和源点连边, f [ i ] = f m a x f[i]=f_{max} f[i]=fmax 的和汇点连边,满足 a i ≤ a j a_i\leq a_j ai≤aj 且 f [ j ] = f [ i ] + 1 f[j]=f[i]+1 f[j]=f[i]+1 的, i i i 向 j j j 连边。流量都是 1 1 1,为了限制每个元素只能用 1 1 1 次,我们把每个点拆开,中间连 1 1 1 条流量为 1 1 1 的边即可,跑最大流就是答案。
第三问的话加几条边,源点向 1 1 1 连, 1 1 1 的分身之间连, f [ n ] f[n] f[n] 如果等于 f m a x f_{max} fmax 那么 n n n 与汇点连, n n n 的分身之间连,这些边流量无限。然后在第二问跑完的图的基础上继续跑即可,答案要加上第二问的答案。(当然,重新建图再跑也行)
以及,当第一问答案为 1 1 1 时,剩下两个问的答案就是 n n n,别忘了这个特判。
代码如下:
#include
#include
#include
using namespace std;
#define maxn 2010
#define inf 999999999
int n,a[maxn],f[maxn],max_f=0,tou,wei;
struct edge{int y,z,next;};
edge e[300010];
int first[maxn],len=1;
void buildroad(int x,int y,int z)
{
e[++len]=(edge){y,z,first[x]};
first[x]=len;
}
int h[maxn],q[maxn],st,ed;
bool bfs()
{
memset(h,0,sizeof(h));
st=ed=1;q[st]=tou;h[tou]=1;
while(st<=ed)
{
int x=q[st++];
for(int i=first[x];i;i=e[i].next)
if(h[e[i].y]==0&&e[i].z>0)h[e[i].y]=h[x]+1,q[++ed]=e[i].y;
}
return h[wei]>0;
}
int dfs(int x,int flow)
{
if(x==wei)return flow;
int tt=0;
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y;
if(h[y]==h[x]+1&&e[i].z>0&&flow-tt>0)
{
int p=dfs(y,min(e[i].z,flow-tt));tt+=p;
e[i].z-=p;e[(i^1)].z+=p;
}
}
if(!tt)h[x]=0; return tt;
}
void work()
{
for(int i=1;i<=n;i++)
{
if(f[i]==1)buildroad(tou,i,1),buildroad(i,tou,0);
if(f[i]==max_f)buildroad(i+n,wei,1),buildroad(wei,i+n,0);
}
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]<=a[j]&&f[i]+1==f[j])buildroad(i+n,j,1),buildroad(j,i+n,0);
for(int i=1;i<=n;i++)buildroad(i,i+n,1),buildroad(i+n,i,0);
int ans=0;
while(bfs())ans+=dfs(tou,inf);
printf("%d\n",ans);
buildroad(tou,1,inf);buildroad(1,tou,0);
buildroad(1,1+n,inf);buildroad(1+n,1,0);
if(f[n]==max_f)buildroad(n+n,wei,inf),buildroad(wei,n+n,0);
buildroad(n,n+n,inf);buildroad(n+n,n,0);
while(bfs())ans+=dfs(tou,inf);
printf("%d\n",ans);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
if(a[j]<=a[i])f[i]=max(f[i],f[j]+1),max_f=max(max_f,f[i]);
printf("%d\n",max_f);
if(max_f==1)return printf("%d\n%d\n",n,n),0;
tou=2*n+1;wei=tou+1;
work();
}