网络流 24 题 最长递增子序列
Posted aaddvvaanntteezz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流 24 题 最长递增子序列相关的知识,希望对你有一定的参考价值。
题解
- 首先对序列做一个简单$dp$求出以每个位置为末尾的最长上升子序列长度$dp_i$,求出最长上升子序列长度$k$,然后考虑如何建图
- 对于每个$dp_i=1$的位置从源点连一条流量为1的边
- 对于每个$dp_i=k$的位置向汇点连一条流量为1的边
- 对于每个位置$i$,向满足$j>i,dp_j=dp_i+1,a_j>a_i$的位置连一条流量为1的边
- 但是这样每个点可能会被选择多次,所以可以对每个位置进行拆点,拆为入点$i$和出点$i+n$入点连入边,出点连出边,入点和出点之间连接流量为1的边这样就能保证每个点只被选择一次
- 对于第3个问题,只需要将1和n与源点和汇点的流量以及它们的入点和出点之间的流量改为无穷大
- 建完图跑最大流即可
- 注意特判序列是单调递减的情况
查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
ll qpow(ll a, ll b)
{
ll res = 1;
for (; b; b >>= 1)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
}
return res;
}
struct graph
{
int head[maxn], nxt[maxn << 1], to[maxn << 1], w[maxn << 1], sz;
void init()
{
memset(head, -1, sizeof(head));
sz = 0;
}
graph() { init(); }
void push(int a, int b, int c) { nxt[sz] = head[a], to[sz] = b, w[sz] = c, head[a] = sz++; }
int &operator[](const int a) { return to[a]; }
} g;
int d[1005], now[1005];
int s, t;
bool bfs()
{
memset(d, 0, sizeof(d));
queue<int> q;
q.push(s);
d[s] = 1;
now[s] = g.head[s];
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = g.head[x]; ~i; i = g.nxt[i])
{
if (!d[g[i]] && g.w[i])
{
d[g[i]] = d[x] + 1;
now[g[i]] = g.head[g[i]];
q.push(g[i]);
if (g[i] == t)
return 1;
}
}
}
return 0;
}
int dinic(int x, int flow)
{
if (x == t)
return flow;
int res = flow, i, k;
for (i = now[x]; ~i && res; i = g.nxt[i])
{
if (d[g[i]] == d[x] + 1 && g.w[i])
{
k = dinic(g[i], min(res, g.w[i]));
if (!k)
d[g[i]] = 0;
res -= k;
g.w[i] -= k;
g.w[i ^ 1] += k;
}
}
now[x] = i;
return flow - res;
}
int dp[505], a[505];
int main()
{
#ifndef ONLINE_JUDGE
freopen("simple.in", "r", stdin);
freopen("simple.out", "w", stdout);
#endif
int n, k = 0;
scanf("%d", &n);
s = 0, t = n * 2 + 1;
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i)
{
dp[i] = 1;
for (int j = 1; j < i; ++j)
{
if (a[i] >= a[j])
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
k = max(k, dp[i]);
}
if (k == 1)
{
printf("%d
%d
%d
", k, n, n);
return 0;
}
for (int i = 1; i <= n; ++i)
{
g.push(i, i + n, 1), g.push(i + n, i, 0);
if (dp[i] == 1)
{
g.push(s, i, 1), g.push(i, s, 0);
}
if (dp[i] == k)
{
g.push(i + n, t, 1), g.push(t, i + n, 0);
}
for (int j = 1; j < i; ++j)
{
if (a[i] >= a[j] && dp[i] == dp[j] + 1)
{
g.push(n + j, i, 1), g.push(i, n + j, 0);
}
}
}
int flow, ans2 = 0;
while (bfs())
{
while (flow = dinic(s, 1e9))
ans2 += flow;
}
g.init();
for (int i = 1; i <= n; ++i)
{
int w = (i == 1 || i == n) ? 1e9 : 1;
g.push(i, i + n, w), g.push(i + n, i, 0);
if (dp[i] == 1)
{
g.push(s, i, w), g.push(i, s, 0);
}
if (dp[i] == k)
{
g.push(i + n, t, w), g.push(t, i + n, 0);
}
for (int j = 1; j < i; ++j)
{
if (a[i] >= a[j] && dp[i] == dp[j] + 1)
{
g.push(n + j, i, 1), g.push(i, n + j, 0);
}
}
}
int f, ans3 = 0;
while (bfs())
{
while (f = dinic(s, 1e9))
ans3 += f;
}
cout << k << endl
<< ans2 << endl
<< ans3 << endl;
return 0;
}
以上是关于网络流 24 题 最长递增子序列的主要内容,如果未能解决你的问题,请参考以下文章