最长不下降子序列问题
题面
题解
最长不下降子序列就直接跑动态规划即可。
数量问题考虑网络流。
把每个点拆成左右两个点。
如果一个数是最长不下降子序列的启示,即 \(dp\) 值为 \(1\)。那么把源点连向它。然后每个数的左点连向右点。
对于在数 \(x\) 后且比数 \(x\) 大的数 \(y\),将 \(y\) 的右点连向 \(x\) 的左点。
对于处于最长不下降子序列的末尾的数,即 \(dp\) 值为 \(max\),把它的右点连向汇点。
模拟一下后会发现,一条增广路即为一个最长不下降子序列。
把边的流量都设为 \(1\),那么跑最大流之后不仅保证了不会重复使用数,而且还找出了最多的数量。
而对于第三个询问,直接把源点流向 \(1\) 的边, \(1\) 左右点之间互相连的边设为正无穷,如果 \(n\) 与汇点相连则进行类似操作。
注意 \(n\) 等于 \(1\) 的情况,第三个询问特判一下,输出一。
代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 505, M = 2e6 + 5;
int n, s, a[N], f[N], S, T, maxflow = 0;
int head[M], nex[M], to[M], w[M], tot = 1;
inline void add(int u, int v, int f) {
nex[++tot] = head[u]; to[tot] = v; w[tot] = f; head[u] = tot;
nex[++tot] = head[v]; to[tot] = u; w[tot] = 0; head[v] = tot;
}
namespace Dinic {
int dep[N << 1], now[N << 1];
queue < int > q;
inline bool bfs() {
while(!q.empty()) q.pop();
memset(dep, 0, sizeof dep);
dep[S] = 1, now[S] = head[S]; q.push(S);
while(!q.empty()) {
int x = q.front(); q.pop();
for(int i = head[x]; i; i = nex[i]) {
if(!w[i] || dep[to[i]]) continue;
dep[to[i]] = dep[x] + 1;
now[to[i]] = head[to[i]];
q.push(to[i]);
if(to[i] == T) return true;
}
}
return false;
}
int dinic(int x, int flow) {
if(x == T) return flow;
int rest = flow, i, k;
for(i = now[x]; i && rest; i = nex[i])
if(w[i] && dep[to[i]] == dep[x] + 1) {
k = dinic(to[i], min(w[i], rest));
if(!k) dep[to[i]] = 0;
w[i] -= k;
w[i ^ 1] += k;
rest -= k;
}
now[x] = i;
return flow - rest;
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) {
f[i] = 1;
for(int j = 1; j < i; j++)
if(a[i] >= a[j]) f[i] = max(f[i], f[j] + 1);
s = max(s, f[i]);
}
printf("%d\n", s);
S = 0, T = 2 * n + 1;
for(int i = 1; i <= n; i++) if(f[i] == 1) add(S, i, 1);
for(int i = 1; i <= n; i++) if(f[i] == s) add(i + n, T, 1);
for(int i = 1; i <= n; i++) add(i, i + n, 1);
for(int i = 1; i <= n; i++)
for(int j = 1; j < i; j++)
if(a[i] >= a[j] && f[i] == f[j] + 1) add(j + n, i, 1);
while(Dinic::bfs()) maxflow += Dinic::dinic(S, 0x3f3f3f3f);
printf("%d\n", maxflow);
add(S, 1 , 0x3f3f3f3f), add(1, 1 + n, 0x3f3f3f3f);
if(f[n] == s) add(n << 1, T, 0x3f3f3f3f), add(n, n << 1, 0x3f3f3f3f);
while(Dinic::bfs()) maxflow += Dinic::dinic(S, 0x3f3f3f3f);
printf("%d\n", maxflow = n == 1 ? 1 : maxflow);
return 0;
}

浙公网安备 33010602011771号