【小結】最小費用流&最大權閉合圖

  • 最小費用流就是在保證流量F 的前提下,求解最少花費是多少的問題O_O
  • 建圖: 每條邊在最大流基礎上(to,cap,rev) ,增加一個費用(cost)
  • 求解方法: 在最大流中,是按照任意可行路徑,也就是殘餘網絡中的可增廣路徑,向網絡中注入流量,從而在匯點獲得相應流量的策略進行,直到不存在可增廣路。簡單分析後可以知道:
    • 如果原圖的最大流F<F ,則顯然不存在所謂最小費用最大流
    • 將邊的費用看做邊連接兩點的距離,那麼如果存在兩條流量相同的可增廣路徑S1S2(flow1=flow2) ,並且二者的路徑長度(也即路徑上所有邊的費用和)滿足
      (Distance1<Distance2)
      那麼顯然前者是更優的選擇。反之亦然,即費用相同時流量越大越好。於是可貪心選取。
    • 爲什麼不用擔心上述貪心會導致: 由於選擇了更大流量的路徑而影響後面的貪心錯誤呢?原因就和基於增廣路算法的最大流一樣啦,窩們不是加了反向邊麼t_t
  • 基於上述求解方法的思路,窩們只需要每次從源點Source 向匯點Destination 選擇最短路然後增廣其可容納的最大流量即可。
  • 複雜度相關: 如果採用Bellman ˙Ford 求最短路,每次獲得的最少流量爲1 ,於是複雜度爲O(F|V||E|) ,堆優化的Dijstra 可以做到O(F|E|log|V|) 。但Bellman 可以處理負權邊,也就是可以處理費用爲負值的圖
  • 似乎理解了上述,便足夠了,不過還是放兩份分別用DijstraBellman 實現的模板,前者還加入了一個有用的頂點勢標記來處理負權環.
  • 模板1
/* **********************************************

  File Name: min_cost_flow.cpp

  Auther: [email protected]

  Created Time: 2015年08月05日 星期三 13時06分05秒

*********************************************** */
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

const double EPS = 1e-8;
const double PI = acos(-1.0);
typedef pair<int, int> P;

const int INF = 0xfffffff;
const int MAX = 10007;
struct Edge
{
    int to;
    int cap;
    int cost;
    int rev;
};
int V; //[0, V)
vector<Edge> G[MAX];
int h[MAX]; //頂點的勢
int dist[MAX]; //min_distance
int prevv[MAX]; //前驅
int preve[MAX]; //前驅對應邊

inline void add_edge(int from, int to, int cap, int cost)
{
    G[from].push_back((struct Edge){to, cap, cost, (int)G[to].size()});
    G[to].push_back((struct Edge){from, 0, -cost, (int)G[from].size() - 1});
}

int min_cost_flow(int s, int t, int f)
{
    int res = 0;
    memset(h, 0, sizeof(h));
    while (f > 0)
    {
        priority_queue<P, vector<P>, greater<P> > Q;
        fill(dist, dist + V, INF);
        dist[s] = 0;
        Q.push(P(dist[s], s));

        while (!Q.empty())
        {
            P p = Q.top();
            Q.pop();

            int v = p.second;
            if (dist[v] < p.first) continue;
            for (int i = 0; i < (int)G[v].size(); ++i)
            {
                Edge& e = G[v][i];
                if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to])
                {
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    Q.push(P(dist[e.to], e.to));
                }
            }
        }

        if (dist[t] == INF)
        {
            return -1; //no more route to go
        }

        for (int v = 0; v < V; ++v)
        {
            h[v] += dist[v];
        }

        int d = f;
        for (int v = t; v != s; v = prevv[v])
        {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for (int v = t; v != s; v = prevv[v])
        {
            Edge& e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

int main()
{
    return 0;
}
  • 模板2
/* **********************************************

  File Name: min_cost_flow_bellman.cpp

  Auther: [email protected]

  Created Time: 2015年08月05日 星期三 14時29分57秒

*********************************************** */
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

const double EPS = 1e-8;
const double PI = acos(-1.0);
typedef pair<int, int> P;

const int INF = 0xfffffff;
const int MAX = 10007;
struct Edge
{
    int to;
    int cap;
    int cost;
    int rev;
};
int V; //[0, V)
vector<Edge> G[MAX];
int dist[MAX]; //min_distance
int prevv[MAX]; //前驅
int preve[MAX]; //前驅對應邊

inline void add_edge(int from, int to, int cap, int cost)
{
    G[from].push_back((struct Edge){to, cap, cost, (int)G[to].size()});
    G[to].push_back((struct Edge){from, 0, -cost, (int)G[from].size() - 1});
}

int min_cost_flow(int s, int t, int f)
{
    int res = 0;
    while (f > 0)
    {
        fill(dist, dist + V, INF);
        dist[s] = 0;
        bool update = true;
        while (update)
        {
            update = false;
            for (int v = 0; v < V; ++v)
            {
                if (dist[v] == INF) continue;
                for (int i = 0; i < (int)G[v].size(); ++i)
                {
                    Edge& e = G[v][i];
                    if (e.cap > 0 && dist[e.to] > dist[v] + e.cost)
                    {
                        dist[e.to] = dist[v] + e.cost;
                        prevv[e.to] = V;
                        preve[e.to] = i;
                        update = true;
                    }
                }
            }
        }

        if (dist[t] == INF)
        {
            return -1; //no more route to go
        }

        for (int v = 0; v < V; ++v)
        {
            h[v] += dist[v];
        }

        int d = f;
        for (int v = t; v != s; v = prevv[v])
        {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * dist[t];
        for (int v = t; v != s; v = prevv[v])
        {
            Edge& e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

int main()
{
    return 0;
}

簡單補充:最大權閉合圖

  • 建圖:每個點向其必要條件連邊,容量INF ,增加源點和匯點,源點向正權點連邊,容量權值,負權點向匯點連邊,容量權值的絕對值,用該圖跑最大流
    =wi0wimaxflow

補充2:dinic網絡流鄰接表實現

  • 連續若干次用vector 啥的搞粗來的dinic 紛紛跪掉,於是重新整理了一份鄰接表版本
/* **********************************************

  File Name: dinic_list.cpp

  Auther: [email protected]

  Created Time: 2015年08月17日 星期一 15時48分47秒

*********************************************** */
#include <bits/stdc++.h>
using namespace std;

const int INF = 0xfffffff;
//最大頂點數
//max vertix
const int MAX_N = 1024;
//最大邊數.注意: *必須*大於兩倍實際的邊數
//max edges.Warning: it should be TWO TIMES BIGGER than the edges truely exist.
const int MAX_E = 100007;
//隊列容量,儘量開大一點兒,實在開不下,用循環隊列
//max nodes in Queue. Warning: It should be BIG ENOUGH
const int MAX_Q = 1000007;

//邊
struct Edge {
    int to;
    int cap; //capcity, 容量
    int nxt; //pointer, 鏈表的next指針
} G[MAX_E];
static int tot; //total edges. 記錄當前存儲邊數[0, tot)
int head[MAX_N]; //head pointer. 頭指針
int level[MAX_N]; //分層
int iter[MAX_N]; //當前弧
int Q[MAX_Q]; //queue, 隊列

inline void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}

//要保證每一對<邊,反向邊>可以互相異或得到
//In case each pair of edges should be with index <2k,2k+1> for some integer k
inline void add_edge(int u, int v, int c) {
    G[tot].to = v, G[tot].cap = c;
    G[tot].nxt = head[u];
    head[u] = tot++;
    G[tot].to = u, G[tot].cap = 0;
    G[tot].nxt = head[v];
    head[v] = tot++;
}

//返回: 是否還能增廣
//return value: whether there is any roads to gain more flows
bool bfs(int s, int t) {
    int front = 0, rear = 0;
    memset(level, -1, sizeof(level));
    level[s] = 0;
    Q[rear++] = s;
    while (front ^ rear) {
        int p = Q[front++];

        for (int i = head[p]; ~i; i = G[i].nxt) {
            Edge& e = G[i];
            if (level[e.to] == -1 && e.cap > 0) {
                level[e.to] = level[p] + 1;
                Q[rear++] = e.to;
                if (e.to == t) {
                    return true;
                }
            }
        }
    }
    return false;
}

int dfs(int s, int t, int flow) {
    if (s == t) return flow;
    int sum = 0, tp;
    for (int& i = iter[s]; ~i; i = G[i].nxt) {
        Edge& e = G[i];
        if (e.cap > 0 && level[e.to] == level[s] + 1) {
            tp = dfs(e.to, t, min(flow, e.cap));
            flow -= tp;
            e.cap -= tp;
            G[i ^ 1].cap += tp;
            sum += tp;
            if (flow == 0) {
                break;
            }
        }
    }
    return sum;
}

int dinic(int s, int t) {
    int sum = 0, tp;
    while (bfs(s, t)) {
        memcpy(iter, head, sizeof(head));
        while (tp = dfs(s, t, INF)) {
            sum += tp;
        }
    }
    return sum;
}

int main() {
    return 0;
}

順便過掉了兩個被卡若干次的題:

  • hdu4888
/* **********************************************

    File Name: 4888.cpp

    Auther: [email protected]

    Created Time: 2015/8/17 星期一 下午 12:47:01

*********************************************** */
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> P;
const int INF = INT_MAX >> 2;
const int MAX = 1024;
const int MAXE = 400007;
const int MAXQ = 1000007;
struct Edge {
    //int from;
    int to;
    int cap;
    int nxt;
} G[MAXE];
struct Edge2 {
    int to;
    int nxt;
} G2[MAXE];
int head[MAX];
int head2[MAX];
bool vis[MAX];
static int tot;
static int tot2;
int Q[MAXQ]; //queue
int level[MAX];
int iter[MAX];

inline void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}

inline void add_edge(int u, int v, int c) {
    G[tot].to = v;
    G[tot].cap = c, G[tot].nxt = head[u];
    head[u] = tot++;
    G[tot].to = u;
    G[tot].cap = 0, G[tot].nxt = head[v];
    head[v] = tot++;
}

inline void add_edge2(int u, int v) {
    G2[tot2].to = v;
    G2[tot2].nxt = head2[u];
    head2[u] = tot2++;
}

bool bfs(int s, int t) {
    memset(level, -1, sizeof(level));
    level[s] = 0;
    int front = 0, rear = 1;
    Q[front] = s;
    while (front != rear) {
        int p = Q[front++];

        for (auto i = head[p]; ~i; i = G[i].nxt) {
            Edge& e = G[i];
            if (level[e.to] == -1 && e.cap > 0) {
                level[e.to] = level[p] + 1;
                Q[rear++] = e.to;
                if (e.to == t) {
                    return true;
                }
            }
        }
    }
    return false;
}

int dfs(int s, int t, int flow) {
    if (s == t) {
        return flow;
    }
    int sum = 0, tp;
    for (int& i = iter[s]; ~i; i = G[i].nxt) {
        Edge& e = G[i];
        if (level[e.to] == level[s] + 1 && e.cap > 0) {
            tp = dfs(e.to, t, min(flow, e.cap));
            sum += tp;
            e.cap -= tp;
            flow -= tp;
            G[i ^ 1].cap += tp;
            if (flow == 0) {
                break;
            }
        }
    }
    return sum;
}

bool dfs2(int s, int fa) {
    for (int i = head2[s]; ~i; i = G2[i].nxt) {
        Edge2& e = G2[i];
        if (e.to == fa) continue;
        if (vis[e.to]) {
            return true;
        }
        vis[e.to] = true;
        if (dfs2(e.to, s)) {
            return true;
        }
        vis[e.to] = false;
    }
    return false;
}

int dinic(int s, int t) {
    int sum = 0, tmp;
    while (bfs(s, t)) {
        memcpy(iter, head, sizeof(head));
        while (tmp = dfs(s, t, INF)) {
            sum += tmp;
        }
    }
    return sum;
}

inline int read() {
    char c;
    while ((c = getchar()) < '0' || c > '9');
    int x = c - '0';
    while ((c = getchar()) >= '0' && c <= '9') {
        x = (x << 3) + (x << 1) + c - '0';
    }
    return x;
}

void rebuild(int s, int t) {
    memset(head2, -1, sizeof(head2));
    tot2 = 0;
    for (int i = s + 1; i < t; ++i) {
        for (int j = head[i]; ~j; j = G[j].nxt) {
            if (G[j].cap > 0 && G[j].to > s && G[j].to < t) {
                add_edge2(i, G[j].to);
                //printf("add_edge <%d, %d>\n", i, G[j].to);
            }
        }
    }
}

int main() {
    int n, m, k;
    while (~scanf(" %d %d %d", &n, &m, &k)) {
        int s = 0, t = n + m + 1, x;
        tot = 0;
        memset(head, -1, sizeof(head));
        int sum1 = 0, sum2 = 0;
        for (int i = 1; i <= n; ++i) {
            x = read();
            sum1 += x;
            add_edge(s, i, x);
        }
        for (int i = 1; i <= m; ++i) {
            x = read();
            sum2 += x;
            add_edge(n + i, t, x);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                add_edge(i, j + n, k);
            }
        }

        if (sum1 != sum2) {
            puts("Impossible");
            continue;
        }

        int max_flow = dinic(s, t);
        //continue;
        //int max_flow = sum1;
        if (max_flow != sum1) {
            puts("Impossible");
        } else {
            bool flag = false;
            rebuild(s, t);
            memset(vis, false, sizeof(vis));
            for (int i = 1; i <= n; ++i) {
                //printf("gao %d\n", i);
                //vis[i] = true;
                if (dfs2(i, -1)) {
                    flag = true;
                    break;
                }
                vis[i] = false;
            }

            if (flag) {
                puts("Not Unique");
            } else {
                puts("Unique");
                int ans[MAX];
                for (int i = 1; i <= n; ++i) {
                    memset(ans, 0, sizeof(ans));
                    for (int j = head[i]; ~j; j = G[j].nxt) {
                        ans[G[j].to] += k - G[j].cap;
                    }
                    printf("%d", ans[n + 1]);
                    for (int j = 2; j <= m; ++j) {
                        printf(" %d", ans[n + j]);
                    }
                    puts("");
                }
            }
        }
    }
    return 0;
}
  • ural1421
/* **********************************************

  File Name: 1421.cpp

  Auther: [email protected]

  Created Time: 2015年08月17日 星期一 16時11分44秒

*********************************************** */
#include <bits/stdc++.h>
using namespace std;

const int INF = 0xfffffff;
//最大頂點數
//max vertix
const int MAX_N = 256;
//最大邊數.注意: *必須*大於兩倍實際的邊數
//max edges.Warning: it should be TWO TIMES BIGGER than the edges truely exist.
const int MAX_E = 30007;
//隊列容量,儘量開大一點兒,實在開不下,用循環隊列
//max nodes in Queue. Warning: It should be BIG ENOUGH
const int MAX_Q = 1000007;

//邊
struct Edge {
    int to;
    int cap; //capcity, 容量
    int nxt; //pointer, 鏈表的next指針
} G[MAX_E];
static int tot;
int head[MAX_N]; //head pointer. 頭指針
int level[MAX_N]; //分層
int iter[MAX_N]; //當前弧
int Q[MAX_Q]; //queue, 隊列

inline void init() {
    memset(head, -1, sizeof(head));
    tot = 0;
}

inline void add_edge(int u, int v, int c) {
    //printf("add edge %d, %d, %d\n", u, v, c);
    G[tot].to = v;
    G[tot].cap = c;
    G[tot].nxt = head[u];
    head[u] = tot++;
    G[tot].to = u;
    G[tot].cap = 0;
    G[tot].nxt = head[v];
    head[v] = tot++;
}

//返回: 是否還能增廣
//return value: whether there is any roads to gain more flows
bool bfs(int s, int t) {
    int front = 0, rear = 0;
    memset(level, -1, sizeof(level));
    level[s] = 0;
    Q[rear++] = s;
    while (front ^ rear) {
        int p = Q[front++];
        //printf("explore %d\n", p);

        for (int i = head[p]; ~i; i = G[i].nxt) {
            Edge& e = G[i];
            if (level[e.to] == -1 && e.cap > 0) {
                level[e.to] = level[p] + 1;
                Q[rear++] = e.to;
                if (e.to == t) {
                    return true;
                }
            }
        }
    }
    return false;
}

int dfs(int s, int t, int flow) {
    if (s == t) return flow;
    int sum = 0, tp;
    for (int& i = iter[s]; ~i; i = G[i].nxt) {
        Edge& e = G[i];
        if (e.cap > 0 && level[e.to] == level[s] + 1) {
            tp = dfs(e.to, t, min(flow, e.cap));
            flow -= tp;
            e.cap -= tp;
            G[i ^ 1].cap += tp;
            sum += tp;
            if (flow == 0) {
                break;
            }
        }
    }
    return sum;
}

int dinic(int s, int t) {
    int sum = 0, tp;
    while (bfs(s, t)) {
        memcpy(iter, head, sizeof(head));
        while (tp = dfs(s, t, INF)) {
            sum += tp;
        }
    }
    return sum;
}

int main() {
    int n;
    while (~scanf(" %d", &n)) {
        init();
        int s = 0, t = n + n + 1, x;
        int sum = 0;
        for (int i = 1; i <= n; ++i) {
            scanf(" %d", &x);
            sum += x;
            add_edge(s, i, x);
        }
        for (int i = 1; i <= n; ++i) {
            scanf(" %d", &x);
            add_edge(i + n, t, x);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                add_edge(i, j + n, 100);
            }
        }

        int maxflow = dinic(s, t);
        if (maxflow != sum) {
            puts("NO");
        } else {
            puts("YES");
            int flow[MAX_N];
            for (int i = 1; i <= n; ++i) {
                memset(flow, 0, sizeof(flow));
                for (int j = head[i]; ~j; j = G[j].nxt) {
                    if (G[j].to > n) {
                        flow[G[j].to - n] += 100 - G[j].cap;
                    }
                }
                for (int j = 1; j <= n; ++j) {
                    printf("%d%c", flow[j], j == n ? '\n' : ' ');
                }
            }
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章