Dinic算法 P3376 【模板】网络最大流

描述

在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水。这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间。因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没的烦恼(不用担心,雨水会流向附近的一条小溪)。作为一名一流的技师,农夫约翰已经在每条排水沟的一端安上了控制器,这样他可以控制流入排水沟的水流量。
农夫约翰知道每一条排水沟每分钟可以流过的水量,和排水系统的准确布局(起点为水潭而终点为小溪的一张网)。需要注意的是,有些时候从一处到另一处不只有一条排水沟。
根据这些信息,计算从水潭排水到小溪的最大流量。对于给出的每条排水沟,雨水只能沿着一个方向流动,注意可能会出现雨水环形流动的情形。

INPUT FORMAT:

第1行: 两个用空格分开的整数N (0 <= N <= 200) 和 M (2 <= M <= 200)。N是农夫John已经挖好的排水沟的数量,M是排水沟交叉点的数量。交点1是水潭,交点M是小溪。
第二行到第N+1行: 每行有三个整数,Si, Ei, 和 Ci。Si 和 Ei (1 <= Si, Ei <= M) 指明排水沟两端的交点,雨水从Si 流向Ei。Ci (0 <= Ci <= 10,000,000)是这条排水沟的最大容量。

OUTPUT FORMAT:

输出一个整数,即排水的最大流量。

SAMPLE INPUT
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
SAMPLE OUTPUT
50

Dinic邻接数组的做法:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<string>
#define Min(a,b) a<b?a:b
#define Max(a,b) a>b?a:b
#define CL(a,num) memset(a,num,sizeof(a));
#define eps  1e-12
#define inf   0x7fffffff
const double pi  = acos(-1.0);
typedef   __int64  ll;
const int maxn = 300 ;
using namespace std;
int n , m;
int flow[maxn][maxn],dis[maxn] ;//dis[i],表示  到 原点  s 的 层数 

int bfs()// 重新 建 图 (按 层数 建图)
{
    memset(dis,-1,sizeof dis); 
    dis[1] = 0 ;
    queue<int>que;
    que.push(1);
    while(!que.empty())
    {
        int  k = que.front();
        que.pop() ;
        for( int i = 1;i<= n;i++)
        {
            if(flow[k][i] > 0 && dis[i] < 0 )// 如果可以到达但还没有访问
            {
                dis[i] = dis[k]+ 1 ;
                que.push(i) ;
            }
        }
    }
    if(dis[n] > 0) return 1; 
    else return  0 ;//断层
}
int  dfs(int x,int mx)// 查找路径上的最小的流量
{

    int i , a ;
    if(x == n) return mx ;

    for(i = 1;i<= n;i++)
    {
        if(flow[x][i] > 0 && dis[i] == dis[x] + 1  && (a =dfs(i,min(mx,flow[x][i]))))
        {
            flow[x][i] -= a;
            flow[i][x] += a;
            return a ;
        }
    }
    return 0 ;
}
int main()
{
    int i,s,e,c;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        memset(flow,0,sizeof flow);
        for(i = 0 ; i < m;i++)
        {
            scanf("%d%d%d",&s,&e,&c);
            flow[s][e] += c;//容量 
        }
      int ans = 0;
      int res;

      while(bfs())
      {
         while(res = dfs(1,inf)) ans+= res ;
      }
      printf("%d\n",ans);
    }

}

模板题:

题目描述
如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

输入格式:
第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含三个正整数ui、vi、wi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi)

输出格式:
一行,包含一个正整数,即为该网络的最大流。

输入输出样例

输入样例#1:
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
输出样例#1:
50
说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=25

对于70%的数据:N<=200,M<=1000

对于100%的数据:N<=10000,M<=100000

样例说明:

题目中存在3条路径:

4–>2–>3,该路线可通过20的流量

4–>3,可通过20的流量

4–>2–>1–>3,可通过10的流量(边4–>2之前已经耗费了20的流量)

故流量总计20+20+10=50。输出50。

Dinic 模板

#include <iostream>
#include <queue>
#include<cstring>
#include<cstdio>
using namespace std;
const int INF = 0x7fffffff;
int V, E,st,ed,cnt=0;
int level[10005],head[10005];
int Si, Ei, Ci;

struct Edge{
    int it,to,next,c,f;
}edge[200100]; //....

void add_eg(int u,int v,int e) 
{
    cnt++;
    edge[cnt].to=v;
    edge[cnt].c+=e;
    edge[cnt].next=head[u];
    head[u]=cnt;    
}
void add(int u,int v,int e){
    add_eg(u,v,e);
    add_eg(v,u,0);
}
bool dinic_bfs()      //bfs方法构造层次网络
{
    queue<int> q;
    memset(level, 0, sizeof(level));
    q.push(st);
    level[st] = 1;
    int u;
    while (!q.empty()) {
        u = q.front();
        q.pop();
        for (int i=head[u]; i>0; i=edge[i].next) {
            int v=edge[i].to ;
            if (!level[v] && edge[i].c>edge[i].f) {
                level[v] = level[u] + 1;
                q.push(v);
            }
        }
    }
    return level[ed] != 0;                //question: so it must let the sink node is the Mth?/the way of yj is give the sink node's id
}
int dinic_dfs(int u, int cp) {           //use dfs to augment the flow
    int t,tp=0;
    if (u == ed)
        return cp;
    for (int i = head[u]; i >0 && cp-tp>0; i=edge[i].next) {
        int v=edge[i].to;
        if (level[u] + 1 == level[v]){
            if( edge[i].c > edge[i].f ) 
            {
                t = dinic_dfs(v, min(cp, edge[i].c - edge[i].f));//父节点的容量,改变的容量-流量 
                edge[i].f += t;
                edge[i^1].f-= t;//....0-(负数)
                tp+=t; 
            }
        }
    }
    return tp;
}
int dinic() {
    int sum=0, tf=0;
    while (dinic_bfs()) {
        while (tf = dinic_dfs(st, INF))
            sum += tf;
    }
    return sum;
}
int main() {
    scanf("%d%d%d%d", &V, &E, &st, &ed);
        memset(edge, 0, sizeof(edge));
        while (E--) {
            scanf("%d%d%d", &Si, &Ei, &Ci);
            add(Si,Ei,Ci);
        }
        int ans = dinic();
        printf("%d\n", ans); 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值