- 传送门 -
https://www.oj.swust.edu.cn/problem/show/1741
Time Limit: 1000 MS Memory Limit: 65536 KB
Total Submit: 379 Accepted: 67 Page View: 1046
Description
给定正整数序列x1 ,... , xn 。 (1)计算其最长递增子序列的长度s。(严格递增) (2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。 (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长 度为s的递增子序列。 编程任务: 设计有效算法完成(1)(2)(3)提出的计算任务。
Input
由文件input.txt提供输入数据。文件第1 行有1个正整数n(n<400),表示给定序列的长度。接 下来的1 行有n个正整数 x1 , ... , xn。
Output
程序运行结束时,将任务(1)(2)(3)的解答输出到文件output.txt中。第1 行是最长 递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出 的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
Source
- 思路 -
第一问dp.
(设(A_x)表示原数列中第 (x) 个数, (F[x]) 表示以第 (x) 个数结束的最长不降子序列的长度, (s), (t) 表示起点终点)
第二问最大流, 对于(A_i<A_j) 且 (F_i+1==F_j) 且 (i<j) 的点对建 (i o j), 对于(F
_x=1)的点建(s o x), 对于$F_x = ans1 $ (第一问答案)的点建 (x o t), 求最大流.
第三问将(s o 1) 和 (n o t)的容量设为一个极大值, 再求最大流.
细节见代码.
- 代码 -
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 400 + 5;
const int M = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int F[N], A[N];
int HD[N], CUR[N];
int NXT[M], FRM[M], TO[M], V[M];
int DIS[N];
int n, m, ans, sz, cnt, ss, tt;
queue<int> q;
void add(int x, int y) {
TO[sz] = y; FRM[sz] = x; V[sz] = 1;
NXT[sz] = HD[x]; HD[x] = sz++;
TO[sz] = x; FRM[sz] = y; V[sz] = 0;
NXT[sz] = HD[y]; HD[y] = sz++;
}
bool bfs() {
memset(DIS, -1, sizeof (DIS));
DIS[ss] = 0;
q.push(ss);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = HD[u]; i != -1; i = NXT[i]) {
int v = TO[i];
if (DIS[v] < 0 && V[i]) {
q.push(v);
DIS[v] = DIS[u] + 1;
}
}
}
return DIS[tt] > 0;
}
int dfs(int x,int a) {
if (x == tt) return a;
int flow = 0, f;
for (int& i = CUR[x]; i != -1; i = NXT[i]) {
if (V[i] && DIS[TO[i]] == DIS[x] + 1)
if (f = dfs(TO[i], min(a, V[i]))) {
V[i] -= f;
V[i^1] += f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int dinic() {
int flow = 0;
while (bfs()) {
memcpy(CUR, HD, sizeof (HD));
flow += dfs(ss, inf);
}
return flow;
}
int main() {
memset(HD, -1, sizeof (HD));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &A[i]);
F[i] = 1;
for (int j = 1; j < i; ++j) {
if (A[j] < A[i])
F[i] = max(F[i], F[j] + 1);
}
ans = max(ans, F[i]);
}
printf("%d
", ans);
ss = 0, tt = n + 1;
for (int i = 1; i <= n; ++i) {
if (F[i] == 1) add(ss, i);
if (F[i] == ans) add(i, tt);
for (int j = 1; j < i; ++j) {
if (A[j] < A[i] && F[j] + 1 == F[i])
add(j, i);
}
}
printf("%d
", dinic());
for (int i = 0; i < sz; ++i) {
if (TO[i] == 1) V[i] = inf;
else if (FRM[i] == n) V[i] = inf;
else V[i] = (i % 2) ^ 1; //更改以及初始化边容量
}
printf("%d
", dinic());
return 0;
}