第一问dp一下就好
第二问
把一个数分成两个点,i,i+n,
i到i+n建一条边容量为1的边,表示这个数可以选一次
0为源点,2n+1为汇点,0,i建一条边容量为1的边表示有以i开头长度为max的最长不下降序列,
i+n,hui建一条边容量为1的边表示有以i结尾长度为max的最长不下降序列,
跑最大流
第三问 残余网络(表示还剩那些数未选)
建立四条边
add(1,n+1,1e9),add(huan,1,1e9);
add(n,n+n,1e9),add(n+n,hui,1e9);(因为只有n前面有max-1个数小于它时,它才能做最后一个数)
再跑一遍最大流+上上一题答案
#include
#include
#include
using namespace std;
int num[1505],n,f[505],huan,hui;
int tov[20005],tow[20005],h[1505],nex[20005],tp=1;
int dis[1505],q[2000005],vis[1505];
void add(int x,int y,int w)
{
tp++;
tov[tp]=y;
tow[tp]=w;
nex[tp]=h[x];
h[x]=tp;
tp++;
tov[tp]=x;
tow[tp]=0;
nex[tp]=h[y];
h[y]=tp;
}
int dfs()
{
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(q,0,sizeof(q));
int tail=1,head=0;
dis[huan]=0;
vis[huan]=1;
q[tail]=huan;
while(head0)
{
vis[v]=1;
dis[v]=dis[x]+1;
tail++;
q[tail]=v;
}
}
}
return vis[hui];
}
int bfs(int x,int delta)
{
if(x==hui)return delta;
int ret=0;
for(int i=h[x];i;i=nex[i])
{
int v=tov[i];
if(tow[i]>0&&dis[v]==dis[x]+1)
{
int dd=bfs(v,min(delta,tow[i]));
delta-=dd;
tow[i]-=dd;
tow[i^1]+=dd;
ret+=dd;
}
}
return ret;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j=num[j])f[i]=max(f[i],f[j]+1);
}
int ans=0;huan=0,hui=2*n+1;
for(int i=1;i<=n;i++)
{
ans=max(ans,f[i]);
}
printf("%d\n",ans);
for(int i=1;i<=n;i++)
{
add(i,i+n,1);
if(f[i]==ans){add(i+n,hui,1);}
if(f[i]==1){add(huan,i,1);}
}
for(int i=1;i<=n;i++)
for(int j=1;j=num[j]&&f[i]==f[j]+1)
{
add(j+n,i,1);
}
}
int ans2=0;
while(dfs())
{
ans2+=bfs(huan,1e9);
}
printf("%d\n",ans2);
add(1,n+1,1e9),add(huan,1,1e9);add(n,n+n,1e9);
if(f[n]==ans) add(n+n,hui,1e9);
int ans1=0;
while(dfs())
{
ans1+=bfs(huan,1e9);
}
ans1+=ans2;
printf("%d",ans1);
return 0;
}