时间限制:1秒 内存限制:64M
【问题描述】
给定正整数序列x1, .., xn。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和 xn,则从给定序列中最多可取出多少个长度为s的递增子序列。
注意:题目中的“递增”实际上是“非降”,即i < j 且 x[i] <= x[j]。
【输入格式】
第1 行有 1个正整数n(n<500),表示给定序列的长度。
接下来的1 行有 n个正整数x1, …, xn.
【输出格式】
程序运行结束时,将任务(1)(2)(3)的解答输出。
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s的递增子序列个数。
【输入样例】
4
3 6 2 5
【输出样例】
2
2
3
【数据范围】
n<500
【来源】
[网络流和线性规划24题]之六
这道题很难让人想到网络流,因为容易想复杂。
我一开始在想是不是可以拆成n层点来做比较容易,然后发现会重,这是我们下意识的就应该想到拆点,连流量为1的边,这样每个点就肯定只流了一次了。
现在剩下的问题就是各个点直接的边了,我们有一个第一问用的f数组。
f[i]:以第i个元素开头的最长递增子序列长度。
我们从i往每一个f[j]+1==f[i]&&a[j]>=a[i]的点连边,这样就可以保证不漏了,只需要连f[j]+1==f[i]的不用连其他的,因为我们要的是最长的那几条链,如果一旦有一个可以要的你没要,那它就不可能是最长的那一条了(加上那个点可以更长)。
最后从s往每个f[i]==ans1的点连边,每个f[i]==1的点往t连边就可以跑了。
代码如下:
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=2005;
const int inf=200005;
struct edge
{
int u,v,f,c;
};
vector<edge>e;
vector<int>g[maxn];
int s,t,d[maxn],f[maxn],n,cnt=-1,a[maxn],q[maxn],cur[maxn];
void add(int u,int v,int f){
e.push_back((edge){u,v,f,0});
g[u].push_back(++cnt);
e.push_back((edge){v,u,0,0});
g[v].push_back(++cnt);
}
bool bfs()
{
memset(d,0,sizeof(d));
int root=0,frond=0;
q[root++]=s;
d[s]=1;
while(root!=frond)
{
int i=q[frond++];
int tt=g[i].size();
for(int k=0;k<tt;k++)
{
int id=g[i][k],j=e[id].v;
if(d[j]||e[id].f==e[id].c) continue;
d[j]=d[i]+1;
q[root++]=j;
}
}
return d[t];
}
int dfs(int i,int a)
{
if(i==t||a==0) return a;
int flow=0,f,tt=g[i].size();
for(int &k=cur[i];k<tt;k++)
{
int id=g[i][k],j=e[id].v;
if(d[j]==d[i]+1&&(f=dfs(j,min(a,e[id].f-e[id].c))))
{
a-=f;
flow+=f;
e[id].c+=f;
e[id^1].c-=f;
if(!a) break;
}
}
return flow;
}
int dinic()
{
int flow=0;
while(bfs())
{
memset(cur,0,sizeof(cur));
flow+=dfs(s,inf);
}
return flow;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int ans=0,ans2;
for(int i=n;i>=1;i--)
{
f[i]=0;
for(int j=i+1;j<=n;j++) if(a[i]<=a[j]) f[i]=max(f[i],f[j]);
f[i]++;
ans=max(ans,f[i]);
}
printf("%d\n",ans);
s=0,t=n*2+1;
for(int i=1;i<=n;i++)
{
add(i,i+n,1);
if(f[i]==1) add(i+n,t,1);
if(f[i]==ans) add(s,i,1);
for(int j=i+1;j<=n;j++)
if(f[j]+1==f[i]&&a[j]>=a[i]) add(i+n,j,1);
}
ans2=dinic();
printf("%d\n",ans2);
add(1,n+1,inf);
add(n,n*2,inf);
if(f[1]==ans) add(s,1,inf);
if(f[n]==1) add(2*n,t,inf);
ans2+=dinic();
printf("%d\n",ans2);
return 0;
}