[知識點]網絡流進階之轉換對偶圖

零、目錄html

  I、網絡流基礎算法

  II、網絡流進階之轉換對偶圖網絡

  III、網絡流進階之費用流優化

 

1、前言spa

本文爲上一篇文章《網絡流基礎》之續集,一樣3年前已有一篇文章講解轉換對偶圖,這裏再次爲其翻新一次,但願可以更好理解。code

 

2、最小割htm

講網絡流不得不提一個概念——最小割。便於理解,上一篇文章並無將其攪和進來。最小割是什麼呢?如今要求割斷部分路徑上的流量,使從源點沒有任何流量能夠到達匯點,而截取的流量最小值即最小割。咱們再次拿出上次的模型:blog

首先從1至4最直接的20流量必然須要截掉;從1至2理應截取40,但因爲2-3-4路徑上的最大流僅爲10,加上2-4流量爲20,故只需截取30;總計50流量。隊列

看着看着就以爲有意思了——對於任意一條路徑,其可以流通的流量最大值即是咱們須要割掉的流量最小值,即最大流=最小割get

這裏說起最小割的概念,可以更好的理解接下來的內容——轉換對偶圖。先看一道例題。

 

3、例題

首先,裸網絡流的正確性毋庸置疑,此處再也不贅述,由於數據並無容許這種無腦的方式經過,因此咱們如今帶着腦子想一個更好的辦法。

 

4、轉換對偶圖

每一次狼的派遣其本質其割流。本題雖不是傳統網格圖,但一樣能夠看做一種特殊的三角網格圖。目的一樣是求最小割,咱們來先隨手割一刀看是什麼效果——

如圖爲一種非最小割方案,須要割掉33流量。咱們發現,當且僅當全部割線將圖劃分紅兩部分時,源點沒有流量流向匯點。瞭解這一項規律後,知足最小割的方案能夠輕易獲得——將圖中直接與匯點相連的三條邊割掉,能夠最小割爲3 + 5 + 6 = 14,一樣將圖割成兩部分。

如今,咱們可否將這張圖進行必定改進——對於每一次割,能夠理解爲走過一條連通這兩邊兩側區域的邊,其權值即流量,而咱們求的最小割即最小權值。因此如今咱們將原圖轉換一下——將每一塊區域看做一點,兩個區域之間的邊看做兩點之間相連的邊,稱之爲對偶圖

 看起來大功告成,不過咱們忽視了兩個更大的區域。全部邊界彷佛沒法處理?這時咱們將全部左下方的邊界與一個點視做相連,此點稱之爲超級源點;同理將右上方區域視做超級匯點。

 

這樣咱們每一次求最小割,其實本質是在新的對偶圖上跑最短路,起點爲超級源點,終點爲超級匯點。綜上,上述方案即爲:

 

5、代碼

要注意的是,N/M <=1000的條件下,其對偶圖最多會存在6*10^6個節點,Dijkstra算法時間複雜度爲O(n^2),顯然沒法經過;起初我嘗試了SPFA算法,但因爲其複雜度的不穩定,第10個點TLE了;最終修改爲了優先隊列優化的Dijkstra算法,其複雜度爲O(nlogn)

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std; 

#define MAXN 6000005
#define INF 0x3f3f3f3f

int n, m, t, h[MAXN], dis[MAXN], w, o;

struct Edge {
    int v, next, w;
} e[MAXN];

struct Node {
    int n, w;
};

struct cmp {
    bool operator () (Node a, Node b) {
        return a.w > b.w;
    }
};

priority_queue <Node, vector<Node>, cmp> Q;

void add(int u, int v, int w) {
    o++, e[o] = (Edge) {v, h[u], w}, h[u] = o;
    o++, e[o] = (Edge) {u, h[v], w}, h[v] = o;
}

void init() {
    scanf("%d %d", &n, &m), t = (n - 1) * (m - 1) * 2 + 1;
    memset(h, -1, sizeof(h)), memset(dis, INF, sizeof(dis));
    for (int i = 1, tot = 2; i <= n; i++)
        for (int j = 1; j <= m - 1; j++, tot += 2) {
            int u = i == 1 ? t : tot - m * 2 + 1, v = i == n ? 0 : tot; 
            scanf("%d", &w), add(u, v, w);
        }
    for (int i = 1, tot = 1; i <= n - 1; i++) {
        for (int j = 1; j <= m - 1; j++, tot += 2) {
            int u = j == 1 ? 0 : tot - 1;
            scanf("%d", &w), add(u, tot, w);
        }
        scanf("%d", &w), add(tot - 1, t, w);
    }
    for (int i = 1, tot = 1; i <= n - 1; i++)
        for (int j = 1; j <= m - 1; j++, tot += 2) scanf("%d", &w), add(tot, tot + 1, w);
}

void work() {
    Q.push((Node) {0, 0}), dis[0] = 0;
    while (!Q.empty()) {
        Node o = Q.top();
        for (int x = h[o.n]; x != -1; x = e[x].next) {
            int v = e[x].v;
            if (dis[v] > dis[o.n] + e[x].w) dis[v] = dis[o.n] + e[x].w, Q.push((Node) {v, dis[v]});
        }
        Q.pop();
    }
}

int main() {
    init(); 
    work();
    printf("%d", dis[t]);
    return 0;
} 
相關文章
相關標籤/搜索