1906 最长递增子序列问题
题解:
第一问是普通的DP,同时为后面做铺垫。
后面的在《线性规划与网络流24题》里说的要比我好,引用来:
第一问动态规划已经求出F[i],表示以第i位为开头的最长上升序列的长度,求出最长上升序列长度K。
1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。
2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。
3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。
4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。
求网络最大流,就是第二问的结果。把边(<1.a>,<1.b>)(<N.a>,<N.b>)(S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。
另外本题还有费用流的做法,但应该不会比2ms还快吧(求喷)。
代码:
总时间耗费: 2ms
总内存耗费: 364B
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 2000 + 10;
const int INF = 1e9 + 7;
int n, m, s, t;
int a[maxn], f[maxn];
struct Edge {
int from, to, cap, flow;
};
vector<Edge> edges;
vector<int> G[maxn];
void AddEdge(int from, int to, int cap) {
edges.push_back((Edge){from, to, cap, 0});
edges.push_back((Edge){to, from, 0, 0});
int sz = edges.size();
G[from].push_back(sz-2);
G[to].push_back(sz-1);
}
bool vis[maxn];
int d[maxn];
bool BFS() {
queue<int> Q;
Q.push(s);
memset(vis, 0, sizeof(vis));
d[s] = 0;
vis[s] = 1;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
if(!vis[e.to] && e.cap > e.flow) {
vis[e.to] = 1;
d[e.to] = d[u] + 1;
Q.push(e.to);
}
}
}
return vis[t];
}
int cur[maxn];
int DFS(int u, int a) {
if(a == 0 || u == t) return a;
int f, flow = 0;
for(int& i = cur[u]; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
if(d[u] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
flow += f;
e.flow += f;
a -= f;
edges[G[u][i]^1].flow -= f;
if(a == 0) break;
}
}
return flow;
}
void Maxflow() {
if(m == 1) { cout << n << endl; return; }
int flow = 0;
while(BFS()) {
memset(cur, 0, sizeof(cur));
flow += DFS(s, INF);
}
cout << flow << endl;
}
void init() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
s = 0; t = n * 2 + 1;
}
void DP() {
f[n] = 1;
for(int i = n; i >= 1; i--) {
f[i] = 1;
for(int j = n; j > i; j--) if(a[i] <= a[j]) //mistake2
f[i] = max(f[i], f[j]+1);
m = max(m, f[i]);
}
cout << m << endl;
}
void init_1() {
for(int i = n; i >= 1; i--) {
AddEdge(i, i+n, 1);
if(f[i] == m) AddEdge(s, i, 1);
if(f[i] == 1) AddEdge(i+n, t, 1);
for(int j = i+1; j <= n; j++)
if(f[i] == f[j] + 1 && a[i] <= a[j]) AddEdge(i+n, j, 1); //mistake3
}
}
void init_2() {
edges.clear();
for(int i = s; i <= t; i++) G[i].clear();
for(int i = n; i >= 1; i--) {
int cap = (i == 1 || i == n) ? INF : 1;
AddEdge(i, i+n, cap);
if(f[i] == m) AddEdge(s, i, cap);
if(f[i] == 1) AddEdge(i+n, t, cap);
for(int j = n; j > i; j--)
if(f[i] == f[j] + 1 && a[i] <= a[j]) AddEdge(i+n, j, 1); //mistake4
}
}
int main() {
init();
DP();
init_1();
Maxflow();
init_2();
Maxflow();
return 0;
}