给定正整数序列x1,….. , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1…..xn 。
第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
4
3 6 2 5
2
2
3
第一问DP,然后我就不会做了…
联系到最大流每个流代表一个方案,可以这样建图:
数列每个点拆点,然后连流量为1的边,表示只能选一遍。
对于第一问得到的dp数组,表示以第 i 位结尾的最长递增子串的长度。若 dp[i]=1 ,则s向i连边,容量为1;若 dp[i]=maxlen (也就是第一问答案),则 i′ 向 e 连边,容量为1。
然后若 i>j且a[i]>=a[j]且dp[i]==dp[j]+1 ,则 i′ 向 j 连边,容量为1。
然后跑最大流,每个流表示一个方案。
对于第三问,把与第1个和第n个点相关的边改为INF即可(也就是 <1,1′><n,n′><s,1><n′,e> ),跑一遍最大流即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int INF = 1000000010;
const int SZ = 1000010;
int head[SZ],nxt[SZ],tot = 1,n;
struct edge{
int t,d;
}l[SZ];
void build(int f,int t,int d)
{
l[++ tot].t = t;
l[tot].d = d;
nxt[tot] = head[f];
head[f] = tot;
}
void insert(int f,int t,int d)
{
build(f,t,d); build(t,f,0);
}
int deep[SZ];
queue<int> q;
bool bfs(int s,int e)
{
memset(deep,0,sizeof(deep));
deep[s] = 1;
while(q.size()) q.pop();
q.push(s);
while(q.size())
{
int u = q.front(); q.pop();
for(int i = head[u];i;i = nxt[i])
{
int v = l[i].t;
if(!deep[v] && l[i].d)
{
deep[v] = deep[u] + 1;
q.push(v);
if(v == e) return true;
}
}
}
return false;
}
int dfs(int u,int flow,int e)
{
if(u == e || flow == 0) return flow;
int rest = flow;
for(int i = head[u];i;i = nxt[i])
{
int v = l[i].t;
if(deep[v] == deep[u] + 1 && l[i].d)
{
int f = dfs(v,min(rest,l[i].d),e);
if(f > 0)
{
l[i].d -= f;
l[i ^ 1].d += f;
rest -= f;
if(rest == 0) break;
}
else deep[v] = 0;
}
}
return flow - rest;
}
int dinic(int s,int e)
{
int ans = 0;
while(bfs(s,e)) ans += dfs(s,INF,e);
return ans;
}
int dp[SZ],num[SZ],len = 0;
void init()
{
memset(head,0,sizeof(head));
tot = 1;
}
int ask(int p)
{
init();
int s = n * 2 + 1;
int e = n * 2 + 2;
for(int i = 1;i <= n;i ++)
{
if(i == 1 || i == n)
insert(i,i + n,p);
else
insert(i,i + n,1);
if(dp[i] == 1)
{
if(i == 1)
insert(s,i + n,p);
else
insert(s,i + n,1);
}
if(dp[i] == len)
{
if(i == n)
insert(i + n,e,p);
else
insert(i + n,e,1);
}
}
for(int i = 1;i <= n;i ++)
for(int j = i + 1;j <= n;j ++)
if(num[i] <= num[j] && dp[i] + 1 == dp[j])
insert(i + n,j,1);
return dinic(s,e);
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
scanf("%d",&num[i]);
for(int i = 1;i <= n;i ++)
{
dp[i] = 1;
for(int j = 1;j < i;j ++)
if(num[i] >= num[j])
dp[i] = max(dp[i],dp[j] + 1);
len = max(len,dp[i]);
}
printf("%d\n",len);
printf("%d\n",ask(1));
printf("%d\n",ask(INF));
return 0;
}