[codevs 1906] 最长递增子序列问题
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; }