最大流之最长递增子序列问题
最大流之最長遞增子序列問題
-ACwing 2180
題目描述
給定正整數序列 x1,?,xnx1,?,xnx1,?,xn。
注意:遞增指非嚴格遞增。
輸入格式
第 111 行有 111 個正整數 nnn,表示給定序列的長度。
接下來的 111 行有 nnn 個正整數 x1,?,xnx1,?,xnx1,?,xn。
輸出格式
第 111 行輸出最長遞增子序列的長度 sss。
第 $2 $行輸出可取出的長度為 sss 的遞增子序列個數。
第$ 3 $行輸出允許在取出的序列中多次使用 x1x1x1 和 xnxnxn 時可取出的長度為 sss 的遞增子序列個數。
數據范圍
1≤n≤5001≤n≤5001≤n≤500
輸入樣例:
4 3 6 2 5輸出樣例:
2 2 3題目分析
思路分析
本題作為網絡流24題之一,使用網絡流進行解題。首先是最大非嚴格遞增子序列的大小問題,很容易想到動態規劃來解決,由此可以得出最長的非嚴格遞增子序列。之后,很容易想到將圖分層進行建圖:將第iii層作為當前序列中第iii個數,因此若最長長度為SSS,則需要將圖分為SSS層。之后,連接第i+1i + 1i+1層中序號大于且數字大于等于第iii層的點,之后將源匯點與第一層與最后一層連接即可。
但是此時出現一個問題:如何保證一個點只出現一次?拆點。將一層的點拆為兩層,并只將同層中序號相同的點建立一條流量為1的邊,即可保證每個點僅使用一次,之后便得出第二行答案。第三行答案只需修改第一條邊和最后一條邊流量為無窮即可。
復雜度分析
時間復雜度:建立邊的最大復雜度為O(n3)O(n^3)O(n3),流網絡的復雜度暫時不分析。
空間復雜度:點數量最大為500×500+2=250002500 × 500 + 2 = 250002500×500+2=250002???,邊數量最大??為500×500÷2+498×498+500×2=374004500 × 500 ÷ 2 + 498 × 498 + 500 × 2 = 374004500×500÷2+498×498+500×2=374004,均不超過1e61e61e6。
AC代碼
#include<bits/stdc++.h> typedef long long ll;using namespace std;const int N = 505, M = 1e6 + 10, INF = 1e8; int n, S, T, num[N], maxn; int to[M], nexto[M], head[M], wei[M]; int idx, q[M], pre[M], d[M], dp[N];void add(int a, int b, int c){to[idx] = b, wei[idx] = c, nexto[idx] = head[a], head[a] = idx++;to[idx] = a, wei[idx] = 0, nexto[idx] = head[b], head[b] = idx++; }bool bfs(){int hh = 0, tt = 0;memset(d, -1, sizeof d);q[0] = S, d[S] = 0, pre[S] = head[S];while(hh <= tt){int t = q[hh++];for(int i = head[t]; ~i; i = nexto[i]){int k = to[i];if(d[k] == -1 && wei[i]){d[k] = d[t] + 1;pre[k] = head[k];if(k == T) return true;q[++tt] = k;}}}return false; }int find_t(int u, int limit){if(u == T) return limit;int flow = 0;for(int i = pre[u]; ~i && flow < limit; i = nexto[i]){pre[u] = i;int k = to[i];if(d[k] == d[u] + 1 && wei[i]){int t = find_t(k, min(wei[i], limit - flow));if(!t) d[k] = -1;wei[i] -= t, wei[i ^ 1] += t, flow += t;}}return flow; }int dinic(){int res = 0, flow;while(bfs()){while(flow = find_t(S, INF)) res += flow;}return res; }int main(){ios::sync_with_stdio(false);cin >> n;S = 0, maxn = 1;memset(head, -1, sizeof head);memset(dp, 0, sizeof dp);for(int i = 1; i <= n; i++){cin >> num[i];add(S, i, 1);}dp[1] = 1;for(int i = 2; i <= n; i++){dp[i] = 1;for(int j = 1; j < i; j++){if(num[i] >= num[j]){dp[i] = max(dp[i], dp[j] + 1);maxn = max(maxn, dp[i]);}}}//計算最大遞增子序列T = maxn * n * 2 - 2 * n+ 1;//確定匯點數cout << maxn << endl;if(maxn == 1) cout << n << endl << n << endl;//特判為1的情況,否則1 1的情況會變為無窮else{for(int i = 2; i <= n; i++){for(int j = 1; j < i; j++){if(num[i] >= num[j]){for(int k = 0; k < maxn - 1; k++){add(k * n * 2 + j, k * n * 2 + n + i, 1);}}}}//連接上下兩層for(int i = 1; i <= n; i++) {for(int j = 0; j < maxn - 2; j++){add(j * n * 2 + n + i, j * n * 2 + n + i + n, 1);}add(2 * n * maxn - 3 * n + i, T, 1);}//連接同層邊,限制每個點為1次int cru = dinic();cout << cru << endl;wei[0] = INF, wei[idx - 2] = INF;//更改流量cout << dinic() + cru << endl;}return 0; }總結
以上是生活随笔為你收集整理的最大流之最长递增子序列问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信用卡还款以后多久可以刷出来 信用卡还款
- 下一篇: 代码记录-连边树