题目描述
«问题描述:
给定正整数序列x1,...,xn 。
(1)计算其最长不下降子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
输入输出格式
输入格式:
第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。
输出格式:
第1 行是最长不下降子序列的长度s。第2行是可取出的长度为s 的不下降子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的不下降子序列个数。
输入输出样例
输入样例#1:
4
3 6 2 5
输出样例#1:
2
2
3
说明
n\le 500n≤500
解题思路
第一问用dp求出最大不下降子序列长度ss,第二问第三问用最大流解。 把i节点拆成两部分i1,i2,自建源点s,汇点t,dp[i]为1的节点连(s,i1),dp[i]为ss的连(i2,t)。然后连(i1,i2)。如果dp[i] + 1 == dp[j],那么连边(i2, j1),正向弧都为1,反向弧都为0。假设有一个最长不下降子序列为2 4 (均为下标),因为从i1连出的边只能到i2,流过的弧就为(s,2_1),(2_1,2_2),(2_2,4_1),(4_1,4_2),(4_2, t)。只有dp[i]最大的i2才连接了汇点,所以最大流就是可取出个数。第三问就是把(s,1_1)(1_1,1_2)(n1,n2),以及若dp[n]等于ss的(n2,t)的容量变为无限大。
代码如下
#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>
#include <vector>
#define INF 0x3f3f3f3f
#define s 0
#define t 1002
using namespace std;
int a[505];
int dp[505];
vector<int> g[1005];
struct Line{
int r, w;
bool dir;
Line(int r, int w, int d): r(r), w(w), dir(d){ }
};
vector<Line> line;
int deep[1005];
bool bfs()
{
queue<int> que;
memset(deep, 0, sizeof(deep));
que.push(s);
deep[s] = 1;
while(!que.empty()){
int top = que.front();
que.pop();
for(int i = 0; i < g[top].size(); i ++){
int z = g[top][i];
if(!deep[line[z].r] && line[z].w){
deep[line[z].r] = deep[top] + 1;
que.push(line[z].r);
if(deep[t])
return true;
}
}
}
return false;
}
int dfs(int x, int mix)
{
if(x == t || !mix)
return mix;
int ap = 0;
for(int i = 0; i < g[x].size(); i ++){
int z = g[x][i];
if(deep[x] < deep[line[z].r] && line[z].w){
int p = dfs(line[z].r, min(mix, line[z].w));
ap += p;
mix -= p;
line[z].w -= p;
line[z^1].w += p;
if(!mix)
return ap;
}
}
return ap;
}
int dinic()
{
int ans = 0;
while(bfs())
ans += dfs(s, INF);
return ans;
}
void add_line(int x, int y, int w)
{
line.push_back(Line(y, w, 1));
g[x].push_back(line.size() - 1);
line.push_back(Line(x, 0, 0));
g[y].push_back(line.size() - 1);
}
int main()
{
int n;
while(cin >> n){
for(int i = 1; i <= n; i ++)
cin >> a[i];
for(int i = 1; i <= n; i ++)
dp[i] = 1;
int ss = 0;
for(int i = 1; i <= n; i ++){
for(int j = 1; j < i; j ++){
if(a[i] >= a[j])
dp[i] = max(dp[i], dp[j] + 1);
}
if(dp[i] > ss)
ss = dp[i];
}
cout << ss << endl;
for(int i = 1; i <= n; i ++){
if(dp[i] == 1){
add_line(s, 2*i, 1);
}
if(dp[i] == ss){ //不能else if,因为ss可能等于1
add_line(2*i+1, t, 1);
}
add_line(2*i, 2*i+1, 1);
}
for(int i = 1; i <= n; i ++){
if(dp[i] == ss)
continue;
for(int j = i + 1; j <= n; j ++){
if(dp[i] + 1 == dp[j] && a[i] <= a[j])
add_line(2*i+1, 2*j, 1);
}
}
int q = dinic();
cout << q << endl;
add_line(s, 2, INF);
add_line(2, 3, INF);
add_line(2*n, 2*n+1, INF);
if(dp[n] == ss)
add_line(2*n+1, t, INF);
cout << dinic() + q << endl; //累加的答案
for(int i = s; i <= t; i ++)
g[i].clear();
line.clear();
}
return 0;
}