日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最大流之最长递增子序列问题

發布時間:2023/12/19 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最大流之最长递增子序列问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最大流之最長遞增子序列問題

-ACwing 2180

題目描述

給定正整數序列 x1,?,xnx1,?,xnx1,?,xn

  • 計算其最長遞增子序列的長度 sss
  • 計算從給定的序列中最多可取出多少個長度為 sss 的遞增子序列。(給定序列中的每個元素最多只能被取出使用一次)
  • 如果允許在取出的序列中多次使用 x1x1x1xnxnxn,則從給定序列中最多可取出多少個長度為$ s$ 的遞增子序列。
  • 注意:遞增指非嚴格遞增。

    輸入格式

    111 行有 111 個正整數 nnn,表示給定序列的長度。

    接下來的 111 行有 nnn 個正整數 x1,?,xnx1,?,xnx1,?,xn

    輸出格式

    111 行輸出最長遞增子序列的長度 sss

    第 $2 $行輸出可取出的長度為 sss 的遞增子序列個數。

    第$ 3 $行輸出允許在取出的序列中多次使用 x1x1x1xnxnxn 時可取出的長度為 sss 的遞增子序列個數。

    數據范圍

    1≤n≤5001≤n≤5001n500

    輸入樣例:

    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; }

    總結

    以上是生活随笔為你收集整理的最大流之最长递增子序列问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。