证明增广路算法的正确性及dinic算法的使用

来源:互联网 发布:犀牛软件调整模型尺寸 编辑:程序博客网 时间:2024/05/21 10:57

一直对增广路这种贪心思想表示怀疑,今天看到一个很好的证明。

首先介绍割的概念,所谓图的割,指的是某个顶点集合S属于V,从S出发的所有边的集合成为割(S,V\S),这些边的容量和被称为割的容量,如果有源点s属于S,汇点t属于V\S,则称之为s-t割,如果将s-t割的所有边都在原图中去掉,则不再有s->t的路径。

容易得到,对于任意一个s-t割,总有f的流量<=割的容量,根据平衡条件,当且仅当割为最小割,流为最大流时去等号

首先,对于ford-Fulkerson算法求出的流为f,f对应的残余网络中从s可达的顶点集合为S,因为f对应的残余网络中不存在从s->t的路径了,那么显然,在残余网络中,任意一条从S->

V\S 中的边流量f=c,任意一条从反向弧f=0,这个一定是满足,如果不满足,那么S集合还可以扩充顶点,与前提矛盾。因此,S->V\S的割的容量等于流的大小。则可以证明裸的増广路ford算法是正确的。


再说一下dinic算法,dinic算法总是寻找最短的増广路并沿着它增广,増广路的长度不会在增广过程中改变,则当无法增广时,说明分层图上没有可以增广的路线了,这有两种情况,第一,已经求出了最大流,第二,可能存在长一些的増广路可以继续增广,因此,继续bfs构造分层网络。每次完成后最短増广路长度+1,由于最短路<n,则最对重复n-1次bfs就可完成了。

#include <queue>#include <vector>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int max_node = 20;const int INF = 0x3f3f3f3f;struct Edge{    int to,cap,rev;    Edge(int t,int c,int r):to(t),cap(c),rev(r) {}};vector<Edge> G[max_node];int node_num,edge_num;int dis[max_node]; //各顶点到源点的无权距离,通过BFS得到int cur[max_node]; //当前弧优化void push_edge(int from,int to,int cap){ //直接建立残余网络,插入反向边的方式很巧妙    G[from].push_back( Edge(to,cap,G[to].size()) );    G[to].push_back( Edge(from,0,G[from].size()-1) );}void BFS(){    memset(dis, -1, sizeof(dis));    dis[1]=0;    queue<int> q;    q.push(1);    while(!q.empty()){        int t = q.front(); q.pop();        for(int k = 0; k < G[t].size(); k++){            Edge &e = G[t][k];            if(e.cap > 0 && dis[e.to] == -1){ // 沿着残余网络进行标记                dis[e.to] = dis[t] + 1;                q.push(e.to);            }        }    }}int DFS(int u, int v, int res){    if( u == v) return res;    for(int k = cur[u]; k < G[u].size(); k++){        int to = G[u][k].to;        if( G[u][k].cap > 0 && dis[to] == dis[u] + 1){ //沿着残余网络深度优先搜索            int x = DFS(to, v, min(res, G[u][k].cap));            if(x > 0){                G[u][k].cap -= x;                 int rev = G[u][k].rev;                G[to][rev].cap += x;                cur[u] = k;                return x;            }        }    }    return 0;}int maxFlow(){    int flow = 0;    while(1)    {        memset(cur, 0, sizeof(cur)); //当前弧自然从0开始        BFS(); //构造分层图        if(dis[node_num] == -1){ //最小割出现,找不到增广路            return flow;        }        //在当前分层图上进行最短路增广,如果无法继续增广,则说明当前分层图不存在增广路了,需要重新构造更长路径的分层图        while( int x = DFS(1, node_num, INF) ){            flow += x;        }    }}int main(){#ifdef LOCAL_DEBUG    freopen("input.txt" ,"r", stdin);#endif // LOCAL_DEBUG    int T,cas=1;    scanf("%d" ,&T);    while(T--){        printf("Case %d: ",cas++);        for(int i=0; i<max_node; i++) G[i].clear();        node_num = edge_num = 0;        scanf("%d%d" ,&node_num, &edge_num);        int from, to, cap;        for(int i=1; i<=edge_num; i++){            scanf("%d%d%d" ,&from ,&to, &cap);            push_edge(from, to, cap);        }        printf("%d\n" ,maxFlow());    }}




1 0
原创粉丝点击