最大流首次体验感受——

什么是最大流呢?

从一个出发点(源点),走到一个目标点(汇点),途中可以经过若干条路,每条路有一个权值,表示这条路可以通过的最大流量。

最大流就是从源点到汇点,可以通过的最大流量。

接下来我们看一个图——

图1

这个图中,s是源点,t是汇点。期间可以经过2, 3, 4, 5, 6几个点。每条边上有两个权值,其中第一个表示当前通过这条边的流量,第二个表示这条边最大可以通过的流量。

最佳情况,即最大可以通过的流量的一种情况是这样的——

图2

但还有一种情况,同样可以达到最大流——

图3

这里得到两个结论:

1. 每条路径中都有至少一条边是满的。

2. 最大流可能不止一种情况。

接下来我们再看另一个图:

图4

如果在这个图上找最大流该怎么找?

这样?

图5

不对,这个图乍一看好像满足每条路径上都有一个满流的边这个条件,但是其实还有更大的流——

图6

怎么办呢?

我们可以通过这个方式从图5变到图6——

图7

这里我们的可以这样理解,在我们走出图5 的结果以后,我们允许图中出现图7中的绿色的边,然后我们就得到了绿色的数字所标示出的一条新路,通过这条路径,我们就获得了最大流。

如果我们获得了一个流量图,这个流量图中每条路径上都有一条边是满流了,如何判断这是不是一个最大流的图呢?通过上面的方法,我们在通过某条边之后,在这两个点之间构造一条反向的并且和通过的流量大小相同的边(称为反向边)。这样,就可能产生一条新路,使整个图中的流量增加。那么,我们不断地构造这种边,直到无法寻找到新的路径为止(称为增广路径),是不是就得到了最大流呢?

总结起来,每次找到一条增广路,增广路中每条边的值,都减去路径中,边值最小的边的值(读起来很凹口是不是?多读几遍就好了)。同时,还要给每条边都加上反向边。重复寻找,直到找不到新的路径,我们就获得了这个图的最大流。

注意,

  1. 每次寻找增广路径后,我们都会将原图更改,这样,我们会得到一个新的图。
  2. 在获得最大流的图之前,我们获得的每张图都称为残余网络。原始图也可以视为残余网络。

以上讲的是寻找最大流的思想。

但是,寻找增广路径的方法不止一种。

我最直接想到的方法,使用dfs多次搜索这张图,直到找不到为止。

 #include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std; const int N = ;
const int M = ; int n, m;
int mp[N][N]; //保存地图信息,有向图
bool vis[N][N]; //dfs时标记使用
int ans; //最终结果 void init()
{
memset(mp, , sizeof(mp));
memset(vis, , sizeof(vis));
for(int i = ; i < n; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
mp[a][b] += c;
}
ans = ;
} int dfs(int x, int maxn)
{
if(x == m)
{
ans += maxn; //每次走到汇点后增加的流量
return maxn;
}
int flow = ;
for(int i = ; i <= m; i++)
{
if(mp[x][i] > && !vis[x][i])
{
vis[x][i] = ;
int mmaxn = maxn < mp[x][i] ? maxn : mp[x][i]; //取本路径中所可以通过的最小值
int mid;
if(mmaxn > ) mid = dfs(i, mmaxn); //如果此路仍然是通路,则继续搜索
if(mid > )
{
mp[x][i] -= mid; //已经经过的路要减去耗费的流量
mp[i][x] += mid; //反向路(弧)增加耗费的流量
maxn -= mid; //走过一条通路后剩余的流量
flow += mid; //已经消耗的流量
if(maxn == ) break;
}
}
}
return flow;
} int main()
{
//freopen("test.in", "r", stdin);
while(~scanf("%d%d", &n, &m))
{
init();
while(dfs(, M) > ) memset(vis, , sizeof(vis));
printf("%d\n", ans);
}
return ;
}

dfs

但是后来我突然想到。dfs找到的不一定最短路,每次搜索可能会浪费时间,然后又改成了bfs,这种方法也就是常说的Edmonds-Karp算法。

 #include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int N = ;
const int M = ; int mp[N][N];
int fm[N]; //用于记录路径
int val[N];
int n, m;
int ans; void init()
{
memset(mp, , sizeof(mp));
for(int i = ; i < n; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
mp[a][b] += c;
}
ans = ;
} void work()
{
while()
{
memset(val, , sizeof(val));
val[] = M;
queue<int> que;
que.push();
while(!que.empty()) //spfa,寻找最短路
{
int k = que.front();
que.pop();
if(k == m) break;
for(int i = ; i <= m; i++)
{
if(!val[i] && mp[k][i] > )
{
fm[i] = k;
que.push(i);
val[i] = val[k] < mp[k][i] ? val[k] : mp[k][i];
}
}
}
if(val[m] == ) break; //当前图上找不到源点到汇点的通路,则退出 for(int i = m; i != ; i = fm[i])
{
mp[fm[i]][i] -= val[m]; //经过的路径上要减去耗费的流量
mp[i][fm[i]] += val[m]; //反向路径(弧)增加相应的路径
}
//printf("%5d\n", val[m]);
ans += val[m]; //结果增加新增的流量
}
} void outit()
{
printf("%d\n", ans);
} int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
work();
outit();
}
return ;
}

bfs

接下来又找到了一种看起来很高大上的方法——Dinic算法。

这个方法要说一说,因为我也花了不少时间来理解,虽然还没有完全理解,但是已经被它所包含的思想震撼了。

这个算法是一层一层搜索的。简单来说,就是:

1)将这个图中用bfs遍历一遍,严格确立每个点的层次。

如果使用bfs可以从源点走到汇点,那么执行2),否则这张图中不存在新的增广路,算法结束。

2)从源点开始dfs,寻找到当前图中所有从源点到汇点的路径,在寻找时,严格按照点的层次寻找,只能从第i层的点走到第i+1层的点。

3)重复1)。

好神奇的方法。

 #include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int N = ;
const int M = ; int mp[N][N];
int dis[N];
int cur[N];
bool vis[N];
int n, m;
int ans; void init()
{
memset(mp, , sizeof(mp));
for(int i = ; i < n; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
mp[a][b] += c;
}
ans = ;
} bool bfs()
{
memset(vis, , sizeof(vis));
queue<int> que;
que.push();
dis[] = ;
vis[] = ;
while(!que.empty())
{
int k = que.front();
que.pop();
for(int i = ; i <= m; i++)
{
if(!vis[i] && mp[k][i] > )
{
vis[i] = ;
dis[i] = dis[k]+;
que.push(i);
}
}
}
return vis[m];
} int dfs(int x, int val)
{
if(x == m) return val;
int flow = , minn;
for(int& i = cur[x]; i <= m; i++) //随着i的变化改变cur[x],这样可以节省当前图中下次使用x时耗费的时间
{
int mval = val < mp[x][i] ? val : mp[x][i]; //记录当前路径中的最小的边的权,最后要根据它建立反向边
if(dis[x]+ == dis[i])
{
minn = ;
if(mval > ) minn = dfs(i, mval);
if(minn > )
{
mp[x][i] -= minn;
mp[i][x] += minn;
flow += minn;
val -= minn;
if(val == ) break;
}
} }
return flow;
} void work()
{
while(bfs()) //如果存在增广路,则dfs寻找,否则结束
{
for(int i = ; i <= m; i++) cur[i] = ;
ans += dfs(, M);
}
} void outit()
{
printf("%d\n", ans);
} int main()
{
//freopen("test.in", "r", stdin);
while(~scanf("%d%d", &n, &m))
{
init();
work();
outit();
}
return ;
}

Dinic

【初识——最大流】 hdu 1532 Drainage Ditches(最大流) USACO 93的更多相关文章

  1. POJ 1273 || HDU 1532 Drainage Ditches (最大流模型)

    Drainage DitchesHal Burch Time Limit 1000 ms Memory Limit 65536 kb description Every time it rains o ...

  2. hdu 1532 Drainage Ditches (最大流)

    最大流的第一道题,刚开始学这玩意儿,感觉好难啊!哎····· 希望慢慢地能够理解一点吧! #include<stdio.h> #include<string.h> #inclu ...

  3. hdu 1532 Drainage Ditches(最大流)

                                                                                            Drainage Dit ...

  4. HDU 1532 Drainage Ditches(最大流 EK算法)

    题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=1532 思路: 网络流最大流的入门题,直接套模板即可~ 注意坑点是:有重边!!读数据的时候要用“+=”替 ...

  5. HDU 1532 Drainage Ditches 最大流 (Edmonds_Karp)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1532 感觉题意不清楚,不知道是不是个人英语水平问题.本来还以为需要维护入度和出度来找源点和汇点呢,看 ...

  6. poj 1273 && hdu 1532 Drainage Ditches (网络最大流)

    Drainage Ditches Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 53640   Accepted: 2044 ...

  7. hdu 1532 Drainage Ditches(最大流模板题)

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  8. HDU 1532 Drainage Ditches (网络流)

    A - Drainage Ditches Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64 ...

  9. HDU 1532 Drainage Ditches 分类: Brush Mode 2014-07-31 10:38 82人阅读 评论(0) 收藏

    Drainage Ditches Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  10. HDU 1532 Drainage Ditches (最大网络流)

    Drainage Ditches Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) To ...

随机推荐

  1. 继电器Relay:ZZR08

    继电器常识: 继电器有三个接线柱:常开(NO),常闭(NC),接地(C) 如果连接的时间长,偶尔需要断电, 那么接NC 和 C, 这样继电器set on 时为断电.除此之外,继电器还可以控制按键,以及 ...

  2. Android笔记——导入Github开源项目CircleRefreshLayout

    百度n久都找不到android studio导入第三方类库的正确方法,纠结睡不着 ,最后终于蒙到了方法,原来想太多了  ---------------------------------------- ...

  3. mysql模糊查询like/REGEXP

    原文:mysql模糊查询like/REGEXP 增删改查是mysql最基本的功能,而其中查是最频繁的操作,模糊查找是查询中非常常见的操作,于是模糊查找成了必修课. like模式 like意思是长得像, ...

  4. Dire Wolf ---hdu5115(区间dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5115 题意:有一排狼,每只狼有一个伤害A,还有一个伤害B.杀死一只狼的时候,会受到这只狼的伤害A和这只 ...

  5. SQL语句 递归

    --正向递归查询(根据ID查到自己和自己以下的所有数据)   connect by prior a.id = a. parentid   --反向递归查询(根据叶子ID查出自己和自己以上的根数据)   ...

  6. CentOS系统启动过程1-10 详细叙述

    昨日有个前辈问我,liunx系统是如果启动的,我只是说了个大概,但具体的过程没有理解透彻,今天特意在网上找到下面的流程图,并根据图,进行了详细叙述,如有问题,请指出. 启动第一步--加载BIOS  设 ...

  7. 脉络清晰的BP神经网络讲解,赞

    学习是神经网络一种最重要也最令人注目的特点.在神经网络的发展进程中,学习算法的研究有着十分重要的地位.目前,人们所提出的神经网络模型都是和学习算 法相应的.所以,有时人们并不去祈求对模型和算法进行严格 ...

  8. USACO Section 2.4: Overfencing

    这题因为各种琐事耽耽搁搁做了2天,也出了挺多错误,最后出了一个结论:像这种有对neighbor有通路的图形用一个4个位表示4个方向的int进行位运算比较靠谱. /* ID: yingzho1 LANG ...

  9. PHP中对数据库操作的封装

    在动态网面设计中很多都要涉及到对数据库的操作,但是有时跟据需要而改用其它后台数据库,就需要大量修改程序.这是一件枯燥.费时而且容易出错的功作.其实我们可以用PHP中的类来实现对数据库操作的封装,从而使 ...

  10. Android 的 init.rc 文件简介【转】

    转自:http://blog.csdn.net/yimiyangguang1314/article/details/6268177 init.rc由许多的Action和Service组成.每一个语句占 ...