HDU1532最大流入门

今天学习了网络流,参照挑战程序设计竞赛上的模板把这道题过了。我认为书上给的代码跟讲解有些出入(而且不太通用?)先做一下学习笔记吧。

书上给出Ford-Fullkerson算法的邻接表dfs实现。
从起点开始深度搜索,直到终点为止。并且在搜索的途中不断比较边的容量,保存最小的容量,并且将路径上每条边的最大容量减去最终的最小容量,将反向边的容量加上最终的最小容量。这样第一次构成了残余网络。最后这个dfs函数返回值就是这条路径上的最小容量。
这里写图片描述图片来自http://blog.csdn.net/y990041769/article/details/21026445
括号右面为容量(请忽视左边)

举例 假设第一次走的路线为s-v2-t返回容量值为2.这个时候图已经被重新构造了。s-v2边的容量变为1,v2-t容量变为0,并构造出了v2->s与t->v2的反向边。
构造反向边的目的稍后写出。
在max_flow函数中不断重复着从s点进行深度优先搜索的策略,直到返回值d为0时停止。注意,在第一次搜索后,v2->t之间的容量已经为0,好像是这条通路被割开一样。(这里涉及到了一个最大流等于最小割得概念,在后面进行解释。)所以,再次从这条路搜索时会中断,进而走到别的路上。
比如s->v1->v2遇到阻断,退回v1->t。这条路的最小值为4,返回4.在max_flow 中flow值加4.(v1与t之间的容量变为0)
第三次在max_flow中循环,从s点搜索,会发现已经没有到达t的路径了,dfs返回0,max_flow返回flow,得到了6.这就是从s到t的最大流。
————————————————————————————
最大流最小割
什么是最小割?切断几条边能够使s没有到达t的路径,这些边的容量和最小时,这几条边容量和为最小割。这个图中切得边为v1->t v2->t。其和为6,正好为最大流。在dfs搜索的同时,我们将路径上每条边的容量都要减去这条路径上边的最小容量,这时最小的那条边容量变为0,实现了割去一条路径。在不断循环的过程中,割掉了所有从s到t的路径。每条路径上的割得和加起来就是我们求得最大流。而将最后图中所有容量为零的边割掉,也能求出最小割(因为割得是每条路径上的最小边)这样得出最大流等于最小割。
——————————————————————————————

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<stdlib.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10000
bool used[N];
struct edge{
//终点,容量,反向边的编号
    int to, cap, rev;
};
vector<edge> G[N];
/*这个函数是有点绕的地方(看完dfs注释看括号后面)
e是进入G[from]邻接表的某条边e.rev是e在进入这个邻接表时的G[to].size()
(此时G[to]邻接的边数量)也就是在进行G[to]添加反向边(到起点的边)
,from的位置。
*/
void add(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 });
}
int dfs(int s, int t, int f){
    if (s == t)return f;
    used[s] = true;
    for (int i = 0; i < G[s].size(); i++){
        edge &e = G[s][i];
        if (!used[e.to] && e.cap>0)
        {
            int d = dfs(e.to, t, min(f, e.cap));
            if (d > 0){
                e.cap -= d;
/*e是G[s][i],即s的某条邻接边
e.to指边的终点,e,rev是连接终点起点的边在终点邻接表里的编号(接着看上面说的)*/
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s, int t){
    int flow = 0;
    while (1){
        memset(used, 0, sizeof(used));
        int f = dfs(s, t, INF);
        if (f == 0)return flow;
        flow += f;
    }
}
int main(){
    int V, E;
    while (cin >> E >> V){
        memset(G, 0, sizeof(G));
        for (int i = 0; i < E; i++){
            int a, b, c;
            cin >> a >> b >> c;
            add(a, b, c);
        }
        cout << max_flow(1, V) << endl;
    }
}

Dinic算法

a)原图中的点按照到到源的距离分“层”,只保留不同层之间的边的图

b)根据残量网络计算层次图

c)在层次图中使用DFS 进行增广直到不存在增广路

d)重复以上步骤直到无法增广

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#define min(x,y) ((x<y)?(x):(y))
using namespace std;
const int MAX = 0x5fffffff;//
int tab[250][250];//邻接矩阵 
int dis[250];//距源点距离,分层图 
int q[2000], h, r;//BFS队列 ,首,尾 
int N, M, ANS;//N:点数;M,边数 
int BFS()
{
    int i, j;
    memset(dis, 0xff, sizeof(dis));//以-1填充 
    dis[1] = 0;
    h = 0; r = 1;
    q[1] = 1;
    while (h<r)
    {
        j = q[++h];
        for (i = 1; i <= N; i++)
        if (dis[i]<0 && tab[j][i]>0)
        {
            dis[i] = dis[j] + 1;
            q[++r] = i;
        }
    }
    if (dis[N]>0)
        return 1;
    else
        return 0;//汇点的DIS小于零,表明BFS不到汇点 
}
//Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广 
int find(int x, int low)//Low是源点到现在最窄的(剩余流量最小)的边的剩余流量
{
    int i, a = 0;
    if (x == N)return low;//是汇点 
    for (i = 1; i <= N; i++)
    if (tab[x][i] >0 //联通 
        && dis[i] == dis[x] + 1 //是分层图的下一层 
        && (a = find(i, min(low, tab[x][i]))))//能到汇点(a <> 0) 
    {
        tab[x][i] -= a;
        tab[i][x] += a;
        return a;
    }
    return 0;

}
int main()
{
    int i, j, f, t, flow, tans;
    while (scanf("%d%d", &M, &N) != EOF){
        memset(tab, 0, sizeof(tab));
        for (i = 1; i <= M; i++)
        {
            scanf("%d%d%d", &f, &t, &flow);
            tab[f][t] += flow;
        }
        //
        ANS = 0;
        while (BFS())//要不停地建立分层图,如果BFS不到汇点才结束 
        {
        //  ANS += find(1, 0x7fffffff);
            while (tans = find(1, 0x7fffffff))ANS += tans;
            //一次BFS要不停地找增广路,直到找不到为止,这样写有效率
        }
        printf("%d\n", ANS);
    }
    system("pause");
}

当前弧优化。优化的地方是这个iter数组。他能够避免对一条没有用的边进行检查。
这里写图片描述
当进行完1->2和1->3的深搜后,iter[1]变成了1,即第三次想要进行深搜的时候不会再搜索1->2的路线,避免了对这条无用边的检查。

#include<iostream>
#include<string>
#include<stdlib.h>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define N 1000+3
#define INF 0x3f3f3f3f
#define mem(arr,a) memset(arr,a,sizeof(arr))
int dis[N];
struct edge{
    int to, cap, rev;
};
vector<edge> G[N];
int n, m;
int iter[N];
int level[N];
void add(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 });
}
int bfs(){
    mem(level, -1);
    queue<int>q;
    level[1] = 0;
    q.push(1);
    while (!q.empty()){
        int v = q.front(); q.pop();
        for (int i = 0; i < G[v].size(); i++){
            edge &e = G[v][i];
            if (e.cap>0 && level[e.to] < 0){
                level[e.to] = level[v] + 1;
                q.push(e.to);
            }
        }
    }
    if (level[n] < 0)return 0;
    return 1;
}
int dfs(int x, int f){
    if (x == n)return f;
    for (int &i = iter[x]; i < G[x].size(); i++){
        edge&e = G[x][i]; 
        if (e.cap>0 && level[x] < level[e.to]){
            int d = dfs(e.to, min(f, e.cap));
            if (d>0){
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}


int dinic(){
    int flow = 0;
    int f;
    while (bfs()){
        mem(iter, 0);
        while ((f = dfs(1, INF)) > 0)flow += f;
    }
    return flow;
}
int main(){
    int t;
    cin >> t;
    int cnt = 0;
    while (t--){
        cin >> n >> m;
        mem(G, 0);
        for (int i = 0; i < m; i++){
            int a, b, c;
            cin >> a >> b >> c;
            add(a, b, c);
        }
        printf("Case %d: %d\n", ++cnt, dinic());
    }
}

上面是抄的,下面是自己又重新写了一遍

#include<iostream>
#include<stdlib.h>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
#define N 1000
#define INF 0x3f3f3f3f
#define mem(arr,a) memset(arr,a,sizeof(arr))
int n, m;
int cost[N][N];
int dis[N];
void add(int from, int to, int cap){
    cost[from][to] += cap;
}
int bfs(){
    mem(dis, -1);
    queue<int>q;
    q.push(1);
    dis[1] = 0;
    while (!q.empty()){
        int v = q.front(); q.pop();
        for (int i = 1; i <= m; i++){
            if (dis[i]<0 && cost[v][i]>0){
                dis[i] = dis[v] + 1;
                q.push(i);
            }
        }
    }
    if (dis[m] < 0)return 0;
    else return 1;
}
int dfs(int x,int f){
    if (x == m)return f;
    int flow = 0;
    for (int i = 1; i <= m; i++){
        if (dis[i] == dis[x] + 1 && cost[x][i]>0){
            flow = dfs(i, min(f, cost[x][i]));
            if (flow > 0){
                cost[x][i] -= flow;
                cost[i][x] += flow;
                return flow;
            }
        }
    }
    return 0;
}
int main(){
    while (cin >> n >> m){
        mem(cost, 0);
        for (int i = 0; i < n; i++){
            int a, b, c;
            cin >> a >> b >> c;
            add(a, b, c);
        }
        int f = 0;
        while (bfs())
            f += dfs(1,INF);
        cout << f << endl;
    }
}

二次重写

#include<iostream>
#include<string>
#include<algorithm>
#include<stdlib.h>
#include<stdio.h>
#include<queue>
using namespace std;
#define N 1000+5
#define INF 0x3f3f3f3f
#define mem(arr,a) memset(arr,a,sizeof(arr))
/*********************************/
int t, n, m;
int cost[N][N];
int ranks[N];
int bfs(){
    mem(ranks, -1);
    ranks[1] = 0;
    queue<int>q;
    q.push(1);
    while (!q.empty()){
        int t = q.front(); q.pop();
        for (int i = 1; i <= n; i++){
            if (cost[t][i] > 0 && ranks[i] < 0){
                ranks[i] = ranks[t] + 1;
                q.push(i);
            }
        }
    }
    if (ranks[n]>0)return 1;
    return 0;

}
int dfs(int x,int flow){
    if (x == n)return flow;
    for (int i = 1; i <= n; i++){
        int a;
        if ((cost[x][i] > 0 )&& (ranks[i] == ranks[x] + 1 )&&(a = dfs(i, min(flow, cost[x][i])))){
            cost[x][i] -= a;
            cost[i][x] += a;
            return a;
        }
    }
    return 0;
}
int dinic(){
    int sum = 0;
    while (bfs()){

        while (int a = dfs(1, INF))sum += a;
    }
    return sum;
}
int main(){
    cin >> t;
    int cnt = 1;
    while (t--){
        scanf("%d%d", &n, &m);
        mem(cost, 0);
        for (int i = 0; i < m; i++){
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            cost[a][b] += c;
        }
        printf("Case %d: %d\n",cnt++, dinic());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值