【板子】Dinic算法

關於Dinic算法

  • 引言
    Dinic是求解網絡最大流的經典算法之一,它由最簡單但效率不夠的尋找增廣路算法優化而來。
  • 實現
    1.建圖
    2.BFS構建層次圖,判斷是否存在由源點到匯點的一條當前最短路徑
bool Bfs() {
    memset(lev,0,sizeof(lev));//用於記層次的數組lev[]
    int t; queue<int>q;
    q.push(S),lev[S]=1;
    while(!q.empty()) {
        t=q.front(),q.pop();
        for(int i=hd[t];i;i=nxt[i])
            if(!lev[to[i]]&&c[i]>0) { //每次Dfs進行路徑増廣時更新流量c,需要判斷
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return lev[T];
}

        3.若存在,DFS對源點到匯點上的路徑進行増廣,即找到増廣路上容量最小的路徑容量Fmin ,再將增廣路上的路徑流量進行更新。如此反覆直到無法尋找到増廣路爲止。

int Dfs(int x,int mx) {
    if(x==T||!mx) return mx; //到達終點或無法繼續増廣
    int f;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) { //cur[i]記遍歷點i時的末邊
        cur[x]=i; 
        if(lev[to[i]]==lev[x]+1&&c[i]>0) { //層次相鄰且可以増廣
            if(f=Dfs(to[i],Min(mx,c[i]))) //取Fmin
                return c[i]-=f,c[i^1]+=f,f; //邊i對應反向邊i^1 邊數tot需初始化爲奇數
        }
    }
    return 0;
}

        循環反覆

int Dinic() {
    int f,Flow=0;
    while(Bfs()) {
        memset(cur,0,sizeof(cur));
        while(f=Dfs(S,Inf)) Flow+=f;
    }
    return Flow;
}

        4.否則得到最大流,結束算法

例題

6001. 「網絡流 24 題」太空飛行計劃

題目描述

    W 教授正在爲國家航天中心計劃一系列的太空飛行。每次太空飛行可進行一系列商業性實驗而獲取利潤。現已確定了一個可供選擇的實驗集合 E={E1,E2,⋯,Em} E = { E_1, E_2, \cdots, E_m }E={E​1​​,E​2​​,⋯,E​m​​},和進行這些實驗需要使用的全部儀器的集合 I={I1,I2,⋯,In} I = { I_1, I_2, \cdots, I_n }I={I​1​​,I​2​​,⋯,I​n​​}。實驗 Ej E_jE​j​​ 需要用到的儀器是 I II 的子集 Rj⊆I R_j \subseteq IR​j​​⊆I。

    配置儀器 Ik I_kI​k​​ 的費用爲 ck c_kc​k​​ 美元。實驗 Ej E_jE​j​​ 的贊助商已同意爲該實驗結果支付 pj p_jp​j​​ 美元。W 教授的任務是找出一個有效算法,確定在一次太空飛行中要進行哪些實驗並因此而配置哪些儀器才能使太空飛行的淨收益最大。這裏淨收益是指進行實驗所獲得的全部收入與配置儀器的全部費用的差額。

    對於給定的實驗和儀器配置情況,編程找出淨收益最大的試驗計劃。

輸入格式

    第 1行有 2 個正整數 m 和 n 。m是實驗數,n是儀器數。接下來的 m 行,每行是一個實驗的有關數據。第一個數贊助商同意支付該實驗的費用;接着是該實驗需要用到的若干儀器的編號。最後一行的 n個數是配置每個儀器的費用。

輸出格式

第 行是實驗編號,第 2 行是儀器編號,最後一行是淨收益。

樣例

樣例輸入

10 1 2
25 2 3
5 6 7

樣例輸出

1 2 3
17

數據範圍與提示

1≤n,m≤50

分析

該題正解爲將 求解最大權閉合圖 (轉化)–> 求解最小割 (轉化)–> 求解最大流
具體轉化過程戳這裏 hiho 第119周 最大權閉合子圖

最終最大流的求解使用Dinic算法

代碼

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

const int sm = 105,sn = 2505;
const int Inf = 0x3f3f3f3f;

int N,M,tot=1,sum,S,T,Flow;
int to[sn],hd[sm],nxt[sn],c[sn];
int lev[sm],cur[sm];
int a[sm],b[sm][sm],ct[sm];
bool ex[sm];

int Min(int x,int y) { return x<y?x:y; }
char ch;
void read(int &x) {
    x=0;ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Add(int u,int v,int w) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w;
}
bool Bfs(int S,int T) {
    memset(lev,0,sizeof(lev));
    queue<int>q;
    q.push(S),lev[S]=1;
    while(!q.empty()) {
        int t=q.front();q.pop();
        for(int i=hd[t];i;i=nxt[i]) 
            if(!lev[to[i]]&&c[i]) {
                lev[to[i]]=lev[t]+1;
                if(to[i]==T) return 1;
                q.push(to[i]);
            }
    }
    return lev[T];
}
int Dfs(int x,int mx) {
    if(!mx||x==T) return mx;
    int f=0;
    for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
        cur[x]=i;
        if(c[i]&&lev[to[i]]==lev[x]+1) {
            if(f=Dfs(to[i],Min(mx,c[i])))
                return c[i]-=f,c[i^1]+=f,f;
        }
    }
    return 0;
}
void Dinic() {
    int f=0;
    while(Bfs(S,T)) {
        memset(cur,0,sizeof(cur));
        while(f=Dfs(S,Inf))Flow+=f;
    }
}
void DFS(int x,int b) {
    ex[x]=b;
    for(int i=hd[x];i;i=nxt[i])
        if(c[i]&&!ex[to[i]])
            DFS(to[i],b+1);
}
int main() {
    read(M),read(N);
    for(int i=1;i<=M;++i) {
        read(a[i]);sum+=a[i];
        while(ch!='\n') {
            read(b[i][++ct[i]]); b[i][ct[i]]+=M;
            Add(i,b[i][ct[i]],Inf);
            Add(b[i][ct[i]],i,0);
        }
    }
    for(int i=1;i<=N;++i) read(a[i+M]);
    S=N+M+1; T=S+1;
    for(int i=1;i<=M;++i) 
        Add(S,i,a[i]),Add(i,S,0);
    for(int i=M+1;i<=M+N;++i)
        Add(i,T,a[i]),Add(T,i,0);

    Dinic();    

    DFS(S,1);

    for(int i=1;i<=M;++i)
        if(ex[i]) printf("%d ",i); putchar(10);
    for(int i=1;i<=N;++i)
        if(ex[M+i]) printf("%d ",i); putchar(10);
    printf("%d\n",sum-Flow);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章