题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3549
1、Ford-Fulkerson算法:参见博客详解http://blog.csdn.net/smartxxyx/article/details/9293665/
(1)只利用满足f(e)<c(e)的e或者满足f(e)>0的e对应的反向边rev(e),寻找一条s-t的路径。
(2)如果不存在满足条件的路径,则结束。否则,沿着该路径尽可能地增加流,返回第(1)步。
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#define INF 0x3f3f3f3f
typedef long long LL;
using namespace std;
const int maxn=1008;
struct edge
{
int to,cap,rev;
};
vector<edge>G[maxn];
bool used[maxn];
void addedge(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 v,int t,int f)
{
if(v==t)
return f;
used[v]=true;
for(int i=0;i<G[v].size();i++)
{
edge &e=G[v][i];
if(!used[e.to]&&e.cap>0)
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
int flow=0;
for(;;)
{
memset(used,0,sizeof(used));
int f=dfs(s,t,INF);
if(f==0)
return flow;
flow+=f;
}
}
int main()
{
int n,m,x,y,z,t,T=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&m,&n);
memset(used,false,sizeof(used));
for(int i=0;i<=m;i++)
G[i].clear();
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
int ans=max_flow(1,m);
printf("Case %d: %d\n",++T,ans);
}
return 0;
}
2、Dinic算法:Ford-Fulkerson算法是通过深度优先搜索寻找增广路,并沿着它增广。与之相对,Dinic算法总是寻找最短的增广路,并沿着它增广。因为最短增广路的长度在增广的过程中始终不会变短,所以无需每次都通过宽度预先搜索来寻找最短增广路。我们可以先进行一次宽度优先搜索,然后考虑由近距离顶点指向远距离顶点的所组成的分层图,在上面进行深度优先搜索寻找最短增广路。如果在分层图上找不到新的增广路了,则说明最短增广路的长度确实变长了,或不存在增广路了,于是重新通过宽度优先搜索构造新的分层图。
Dinic算法就是Ford-Fulkerson算法的优化。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define INF 0x3f3f3f3f
typedef long long LL;
using namespace std;
const int maxn=1008;
struct edge
{
int to,cap,rev;
};
int can[maxn][maxn];
vector<edge>G[maxn];
int level[maxn],n,k;//顶点到源点的距离标号
int iter[maxn];//当前弧,在其之前的边已经没有用了
void addedge(int from,int to,int cap)//向图中增加一条从from到to的容量为cap的边
{
G[from].push_back((edge){to,cap,G[to].size()});
G[to].push_back((edge){from,0,G[from].size()-1});
}
void bfs(int s)//通过bfs计算从源点出发的距离标号
{
memset(level,-1,sizeof(level));
queue<int>que;
while(!que.empty())
que.pop();
level[s]=0;
que.push(s);
while(!que.empty())
{
int v=que.front();
que.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;
que.push(e.to);
}
}
}
}
int dfs(int v,int t,int f)//通过dfs寻找增广路
{
if(v==t||f==0)
return f;
for(int &i=iter[v]; i<G[v].size(); i++)
{
edge &e=G[v][i];
if(e.cap>0&&level[v]<level[e.to])
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)//求解从s-t的最大流
{
int flow=0;
for(;;)
{
bfs(s);
if(level[t]<0)
return flow;
memset(iter,0,sizeof(iter));
int f;
while((f=dfs(s,t,INF))>0)
{
flow+=f;
}
}
}
int main()
{
int t,m,x,y,z,T=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
k=n;
for(int i=0; i<=n; i++)
G[i].clear();
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
}
int ans=max_flow(1,n);
printf("Case %d: %d\n",++T,ans);
}
return 0;
}
3、Edmonds-Karp算法:算法详解参见博客http://blog.csdn.net/y990041769/article/details/21026445
Edmonds-Karp算法,即最短路径增广算法,简称EK算法。
其思路是每次找出一条从源到汇的能够增加流的路径,调整流值和残留网络 不断调整直到没有增广路为止
增广路: 增广路是这样一条从s到t的路径,路径上每条边残留容量都为正
把残留容量为正的边设为可行的边 那么我们就可以用简单的BFS得到边数最少的增广路
EK算法的思路非常的简单,就是一直找增广路径(BFS),假如有记录增广路的最小值k,ans+=k,并更新网络的值(要用反向边)。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define INF 0x3f3f3f3f
typedef long long LL;
using namespace std;
const int maxn=1008;
struct edge
{
int from,to,cap,flow;
edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct edmondskarp
{
int n,m;
vector<edge>edges;
vector<int>mp[maxn];
int a[maxn];
int p[maxn];
void init(int n)
{
for(int i=0;i<n;i++)
mp[i].clear();
edges.clear();
}
void addedge(int from,int to,int cap)
{
edges.push_back(edge(from,to,cap,0));
edges.push_back(edge(to,from,0,0));//反向弧
m=edges.size();
mp[from].push_back(m-2);
mp[to].push_back(m-1);
}
int Maxflow(int s,int t)
{
int flow=0;
for(;;)
{
memset(a,0,sizeof(a));
queue<int>q;
q.push(s);
a[s]=INF;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<mp[x].size();i++)
{
edge& e=edges[mp[x][i]];
if(!a[e.to]&&e.cap>e.flow)
{
p[e.to]=mp[x][i];
a[e.to]=min(a[x],e.cap-e.flow);
q.push(e.to);
}
}
if(a[t])
break;
}
if(!a[t])
break;
for(int u=t;u!=s;u=edges[p[u]].from)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
}
flow+=a[t];
}
return flow;
}
};
int main()
{
int t,n,m,T=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
edmondskarp ac;
int x,y,z;
ac.init(n);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&x,&y,&z);
ac.addedge(x,y,z);
}
int ans=ac.Maxflow(1,n);
printf("Case %d: %d\n",++T,ans);
}
return 0;
}
题目描述:
Flow Problem
Time Limit: 5000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 16308 Accepted Submission(s): 7694
For each test case, the first line contains two integers N and M, denoting the number of vertexes and edges in the graph. (2 <= N <= 15, 0 <= M <= 1000)
Next M lines, each line contains three integers X, Y and C, there is an edge from X to Y and the capacity of it is C. (1 <= X, Y <= N, 1 <= C <= 1000)