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