【網絡流 24 題】最長遞增子序列(最多不相交路徑)

鏈接【網絡流 24 題】最長遞增子序列

題目描述

給定正整數序列 ,以下遞增子序列均爲非嚴格遞增。

  1. 計算其最長遞增子序列的長度ss
  2. 計算從給定的序列中最多可取出多少個長度爲ss的遞增子序列。
  3. 如果允許在取出的序列中多次使用x1x_1xnx_n,則從給定序列中最多可取出多少個長度爲ss的遞增子序列。

輸入格式

文件第11行有11個正整數 ,表示給定序列的長度。接下來的11行有nn個正整數x1x_1~xnx_n

輸出格式

11行是最長遞增子序列的長度 。第22行是可取出的長度爲ss的遞增子序列個數。第33行是允許在取出的序列中多次使用x1x_1x2x_2時可取出的長度爲ss的遞增子序列個數。

樣例輸入

4
3 6 2 5

樣例輸出

2
2
3



分析:

在建模前,需要預處理得到dp[i]dp[i]:表示以xix_i結尾可以得到的最長遞增子序列長度,同時可得第一問的答案——最長遞增子序列的長度ss


對每一個點進行拆點,如點uu拆分爲uuu\rarr u'(入點爲uu,出點爲uu'),容量爲1,表示僅可選一次;

對於dp[i]=1dp[i]=1的點,說明可以作爲最長遞增子序列的起點,連邊sis\rarr i,容量爲\infin
對於dp[i]=sdp[i]=s的點,說明可以作爲最長遞增子序列的終點,連邊iti'\rarr t,容量爲\infin

而對於點之間,若dp[i]+1=dp[j]dp[i]+1=dp[j],說明xjx_j可以接在xix_i之後,連邊iji'\rarr j,容量爲1。

跑最大流,即爲第二問答案。


對於第三問,只需將點1和點n的拆點容量改爲\infin即可,但要注意特判s=1s=1的情況。



代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 10;
int n, s = 0, t = maxn - 1;
int a[maxn];
int head[maxn], cnt;
struct edge {
    int w;     //邊的流量(殘量)
    int to;    //該邊通向的結點v
    int next;  //點u鄰接表的下一條邊
} e[maxn];
void add_edge(int u, int v, int w)  //添加一條u->v,最大容量爲w的邊
{
    //建立正向邊
    e[cnt].w = w;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
    cnt++;
    //建立反向邊
    e[cnt].w = 0;  //有些圖是需要建立雙向邊(例如求最小割時),則反向邊的初始殘量不爲0
    e[cnt].to = u;
    e[cnt].next = head[v];
    head[v] = cnt;
    cnt++;
}
int dis[maxn];  // dis數組記錄層次
bool bfs()      //利用BFS建立分成圖,從而可以多次DFS增廣
{
    memset(dis, -1, sizeof(dis));  //初始化dis數組
    queue<int> q;
    q.push(s);
    dis[s] = 0;  //源點層次爲0
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].to;
            if (e[i].w > 0 && dis[v] == -1)  //可達&&未分層
            {
                dis[v] = dis[u] + 1;  //分層
                if (v == t)           //若到達匯點,則分層結束,返回true
                    return true;
                q.push(v);
            }
        }
    }
    return false;  //運行到此處,說明匯點已不可達,返回false
}
int cur[maxn];  //弧優化:cur數組用於記錄上一次DFS增廣時u已經增廣到第幾條邊,從而優化時間
int dfs(int u, int flow)  // flow代表流入u點的最大流量
{
    if (u == t)
        return flow;                                 //到達匯點,直接返回flow
    for (int &i = cur[u]; i != -1; i = e[i].next) {  //注意i前面用&引用,這樣就可以直接改變cur[u]
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].w > 0)  // v爲u的下一層&&可達
        {
            int k = dfs(v, min(flow, e[i].w));
            if (k > 0) {
                e[i].w -= k;      //正向邊-=k
                e[i ^ 1].w += k;  //反向邊+=k
                return k;
            }
        }
    }
    return 0;  //無法繼續增廣,返回0
}
int dinic() {
    int ans = 0;   //記錄總流量
    while (bfs())  //分層
    {
        for (int i = 0; i < maxn; i++)  //初始化cur數組,即將head數組賦給cur數組
            cur[i] = head[i];
        while (int k = dfs(s, INF))  //增廣
            ans += k;
    }
    return ans;
}
int dp[1010], max_s;
void init() {
    memset(head, -1, sizeof(head));
    cnt = 0;
}
int main() {
    init();
    scanf("%d", &n);
    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[j] <= a[i])
                dp[i] = max(dp[i], dp[j] + 1);
        }
        max_s = max(max_s, dp[i]);
    }
    printf("%d\n", max_s);
    for (int i = 1; i <= n; i++) {
        add_edge(i, n + i, 1);
        if (dp[i] == 1)
            add_edge(s, i, INF);
        if (dp[i] == max_s)
            add_edge(n + i, t, INF);
        for (int j = 1; j < i; j++) {
            if (a[j] <= a[i] && dp[j] + 1 == dp[i])
                add_edge(n + j, i, 1);
        }
    }
    printf("%d\n", dinic());
    init();
    for (int i = 1; i <= n; i++) {
        if (i == 1 || i == n)
            add_edge(i, n + i, INF);
        else
            add_edge(i, n + i, 1);
        if (dp[i] == 1)
            add_edge(s, i, INF);
        if (dp[i] == max_s)
            add_edge(n + i, t, INF);
        for (int j = 1; j < i; j++) {
            if (a[j] <= a[i] && dp[j] + 1 == dp[i])
                add_edge(n + j, i, 1);
        }
    }
    if (max_s == 1)
        printf("%d\n", n);  //特判
    else
        printf("%d\n", dinic());
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章