Edmonds-Karp:
http://blog.csdn.net/moon_1st/article/details/5305524
这个网站上讲解了一些概念,可以了解一下。
Edmonds-Karp算法是Ford-Fulkerson方法的bfs实现,每次找增广路,把这条路上的所有点的流量加上这条路上的残余容量,再找新的增广路,直到找不到为止。
在无权边的有向图中寻找最短路,最简单的方法就是广度优先搜索 (BFS),E-K 算法就直接来源于此。每次用一遍 BFS 寻找从源点 s 到终点 t 的最短路作为增广路径,然后增广流量 f 并修改残量网络,直到不存在新的增广路径。
E-K 算法的时间复杂度为 O(VE2),由于 BFS 要搜索全部小于最短距离的分支路径之后才能找到终点,因此可以想象频繁的 BFS 效率是比较低的。
下面时代码,其中fa存储的是增光路径,f是记录流量的相邻矩阵,c是记录容量的矩阵,需要注意的是queue队列不要开成全局变量:
void Edmonds_Karp()
{
int i,d;
long long ans=0;
int s;
fa[m]=1;
while(fa[m]!=0)
{
queue<int> zui;
zui.push(1);
memset(fa,0,sizeof(fa));
fa[1]=1;
while(!zui.empty()&&fa[m]==0)
{
i=zui.front();
zui.pop();
for(int j=1; j<=m; j++)
{
if(fa[j]==0)
if(f[i][j]<c[i][j])
{
fa[j]=i;
zui.push(j);
}
else if(f[j][i]>0)
{
fa[j]=-i;
zui.push(j);
}
}
}
if(fa[m]!=0)
{
i=m;
d=INT_MAX;
while(i!=1)
{
if(fa[i]>0)
{
if(c[fa[i]][i]-f[fa[i]][i]<d)
d=c[fa[i]][i]-f[fa[i]][i];
}
else if(f[i][-fa[i]]<d)
d=f[i][-fa[i]];
i=abs(fa[i]);
}
ans+=d;
i=m;
while(i!=1)
{
if(fa[i]>0)
f[fa[i]][i]+=d;
else
f[i][-fa[i]]-=d;
i=abs(fa[i]);
}
}
}
}
#include<cstdio> //EK()算法。时间复杂度(VE^2)
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 100;
const int INF = (1<<30)-1;
int g[maxn][maxn];
int flow[maxn],pre[maxn];
bool vis[maxn];
int n,m;
int bfs(int s,int e){
memset(pre,-1,sizeof(pre));
memset(vis,false,sizeof(vis));
queue<int > q;
vis[s] = true;
for(int i=1;i<=n;i++) flow[i]=INF;
q.push(s);
while(!q.empty()){
int now = q.front(); q.pop();
if(now==n) break;
for(int i=1;i<=n;i++){ //寻找增广路最小流量
if(!vis[i]&&g[now][i]>0){
vis[i] = true;
flow[i] = min(flow[now],g[now][i]);
pre[i] = now;
q.push(i);
}
}
}
if(!vis[e]|| e==1) //找不到完整的增广路or源点汇点重合
return -1;
else
return flow[e];
}
int EK(int s,int e){
int temp,d,res,maxflow;
maxflow = 0;
while((d=bfs(s,e))!=-1){
maxflow += d;
temp=n;
while(temp!=1){
res = pre[temp];
g[res][temp]-=d; //正向边
g[temp][res]+=d; //反向边
temp = res;
}
}
return maxflow;
}
int main(){
int T,ca=1;
int start,end,capacity;
scanf("%d",&T);
while(T--){
memset(g,0,sizeof(g));
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&start,&end,&capacity);
g[start][end]+=capacity;
}
printf("Case %d: %d\n",ca++,EK(1,n));
}
return 0;
}
const int INF=999999999;
int c[110][110],f[110][110],pre[110];
int q[110],d[110];
int n,m,s,t,res;
bool bfs()
{
int i,j,tail=1,head=0;
q[tail]=s;
pre[s]=-1;
memset(d,0,sizeof(d));
d[s]=INF;
while(head<tail)
{
i=q[++head];
for(j=1;j<=n*2;j++)
{
if(c[i][j]>f[i][j]&&!d[j])
{
d[j]=min(d[i],c[i][j]-f[i][j]);
pre[j]=i;
q[++tail]=j;
if(j==t)
return true;
}
}
}
return false;
}
int EK()
{
res=0;
while(bfs())
{
for(int i=t;i!=s;i=pre[i])
{
f[pre[i]][i]+=d[t];
f[i][pre[i]]-=d[t];
}
res+=d[t];
}
return res;
}
#include<cstdio> //EK()算法。时间复杂度(VE^2)
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 100;
const int INF = (1<<30)-1;
int g[maxn][maxn];
int flow[maxn],pre[maxn];
bool vis[maxn];
int n,m;
int bfs(int s,int e){
memset(pre,-1,sizeof(pre));
memset(vis,false,sizeof(vis));
queue<int > q;
vis[s] = true;
for(int i=1;i<=n;i++) flow[i]=INF;
q.push(s);
while(!q.empty()){
int now = q.front(); q.pop();
if(now==n) break;
for(int i=1;i<=n;i++){ //寻找增广路最小流量
if(!vis[i]&&g[now][i]>0){
vis[i] = true;
flow[i] = min(flow[now],g[now][i]);
pre[i] = now;
q.push(i);
}
}
}
if(!vis[e]|| e==1) //找不到完整的增广路or源点汇点重合
return -1;
else
return flow[e];
}
int EK(int s,int e){
int temp,d,res,maxflow;
maxflow = 0;
while((d=bfs(s,e))!=-1){
maxflow += d;
temp=n;
while(temp!=1){
res = pre[temp];
g[res][temp]-=d; //正向边
g[temp][res]+=d; //反向边
temp = res;
}
}
return maxflow;
}
int main(){
int T,ca=1;
int start,end,capacity;
scanf("%d",&T);
while(T--){
memset(g,0,sizeof(g));
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&start,&end,&capacity);
g[start][end]+=capacity;
}
printf("Case %d: %d\n",ca++,EK(1,n));
}
return 0;
}
const int INF=999999999;
int c[110][110],f[110][110],pre[110];
int q[110],d[110];
int n,m,s,t,res;
bool bfs()
{
int i,j,tail=1,head=0;
q[tail]=s;
pre[s]=-1;
memset(d,0,sizeof(d));
d[s]=INF;
while(head<tail)
{
i=q[++head];
for(j=1;j<=n*2;j++)
{
if(c[i][j]>f[i][j]&&!d[j])
{
d[j]=min(d[i],c[i][j]-f[i][j]);
pre[j]=i;
q[++tail]=j;
if(j==t)
return true;
}
}
}
return false;
}
int EK()
{
res=0;
while(bfs())
{
for(int i=t;i!=s;i=pre[i])
{
f[pre[i]][i]+=d[t];
f[i][pre[i]]-=d[t];
}
res+=d[t];
}
return res;
}
Dinic算法(借鉴的别人的):
算法步骤:
Dinic算法的思想也是分阶段地在层次图中增广。它与最短路径增值算法不同之处是:在Dinic算法中,我们用一个dfs过程代替多次bfs来寻找阻塞流。下面给出其算法步骤:
- 初始化流量,计算出剩余图
- 根据剩余图计算层次图。若汇点不在层次图内,则算法结束
- 在层次图内用一次dfs过程增广
- 转步骤2
- 用BFS建立分层图
注意:分层图是以当前图为基础建立的,所以要重复建立分层图 - 用DFS的方法寻找一条由源点到汇点的路径,获得这条路径的流量I 根据这条路径修改整个图,将所经之处正向边流量减少I,反向边流量增加I,注意I是非负数
- 重复步骤2,直到DFS找不到新的路径时,重复步骤1
- 初始化流量,计算出剩余图
- 根据剩余图计算层次图。若汇点不在层次图内,则算法结束
- 在层次图内用一次dfs过程增广
- 转步骤2
注意(可以无视):
- Dinic(其实其他的好多)算法中寻找到增广路后要将反向边增加I
- Dinic中DFS时只在分层图中DFS,意思是说DFS的下一个节点的Dis(距源点的距离)要比自己的Dis大1,例如在图1中第一个次DFS中,1->2->4 这条路径是不合法的,因为Dis[2]=1;Dis[4]=1;
下面是dfs的过程:
ps;
While outdegree(s)>0
up.top;
if u<>t
if outdegree(u)>0
设(u,v)为层次图中的一条边;
pp,v;
else
从p和层次图中删除点u,
以及和u连接的所有边;
else
增广p(删除了p中的饱和边);
令p.top为p中从s可到达的最后顶点;
end while
在程序里,p表示找到的增广路径,p.top为路径中的最后一个顶点。一开始,p中只有源点。
整个While循环分为2个操作。如果p的最后一个顶点为汇点,也就是说找到了增广路,那么对p增广,注意到增广后一定有一条或多条p中的边被删除了。这时,我们使增广路径后退至p中从源点可到达的最后一个顶点。
如果p的最后一个顶点不为汇点,那么观察最后那个的顶点u 。若在层次图中存在从u连出的一条边,比如(u,v),我们就将顶点v放入路径p中,继续dfs遍历;否则,点u对之后的dfs遍历就没有用了,我们将点u以及层次图中连到u的所有边删除,并且在p中后退一个点。
Dfs过程将会不断重复这2个操作,直到从源点连出的边全部被删除为止。
二、Dinic 算法模板
void Dinic()
{
for(;;){
BFS();
if(D[T]==-1)break;
int path_n=0;
int x=S;
memcpy(cur,E,sizeof(cur));
for(;;){
if(x==T){
int mink=-1,delta=INT_MAX;
for(int i=0;i<path_n;++i){
if(path[i]->c<delta){
delta=path[i]->c;
mink=i;
}
}
for(int i=0;i<path_n;++i){
path[i]->c-=delta;
path[i]->back->c+=delta;
}
path_n=mink;
x=path[path_n]->x;
}
edge* e;
for(e=cur[x];e;e=e->next){
if(e->c==0)
continue;
int y=e->y;
if(D[x]+1==D[y])
break;
}
cur[x]=e;
if(e){
path[path_n++]=e;
x=e->y;
}
else{
if(path_n==0)
break;
D[x]=-1;
--path_n;
x=path[path_n]->x;
}
}
}
}
#include<iostream>
#define Max 210
int flow[Max][Max],d[Max]; //flow is the network
int sta,end,N; //sta is the sourse ,end is the,N is the number of vector
bool bfs(int s)
{
int front=0,rear=0;
int q[Max];
memset(d,-1,sizeof(d)); //d[] is the deep
q[rear++]=s;
d[s]=0;
while(front<rear)
{
int k=q[front++];
for(int i=0; i<=N; i++)
if(flow[k][i]>0&&d[i]==-1)
{
d[i]=d[k]+1;
q[rear++]=i;
}
}
if(d[end]>=0) return true;
return false;
}
int dinic(int k,int sum) //k is the sourse
{
if(k==end)
return sum;
int os=sum;
for(int i=0; i<=N&∑ i++)
if(d[i]==d[k]+1&&flow[k][i]>0)
{
int a=dinic(i,min(sum,flow[k][i])); //Deep to the end.
flow[k][i]-=a;
flow[i][k]+=a;
sum-=a;
}
return os-sum;
}
int main()
{
int ret=0;
while(bfs(sta))
ret+=dinic(sta,INT_MAX);
printf("%d\n",ret);
return 0;
}
还有一个更高效的算法,据说是没有数据能卡住,SAP。
#include<iostream>
#define Max 210
int flow[Max][Max],d[Max]; //flow is the network
int sta,end,N; //sta is the sourse ,end is the,N is the number of vector
bool bfs(int s)
{
int front=0,rear=0;
int q[Max];
memset(d,-1,sizeof(d)); //d[] is the deep
q[rear++]=s;
d[s]=0;
while(front<rear)
{
int k=q[front++];
for(int i=0; i<=N; i++)
if(flow[k][i]>0&&d[i]==-1)
{
d[i]=d[k]+1;
q[rear++]=i;
}
}
if(d[end]>=0) return true;
return false;
}
int dinic(int k,int sum) //k is the sourse
{
if(k==end)
return sum;
int os=sum;
for(int i=0; i<=N&∑ i++)
if(d[i]==d[k]+1&&flow[k][i]>0)
{
int a=dinic(i,min(sum,flow[k][i])); //Deep to the end.
flow[k][i]-=a;
flow[i][k]+=a;
sum-=a;
}
return os-sum;
}
int main()
{
int ret=0;
while(bfs(sta))
ret+=dinic(sta,INT_MAX);
printf("%d\n",ret);
return 0;
}
SAP算法模板:
http://blog.csdn.net/lhshaoren/article/details/7795342
typedef struct node{
int v, w;
struct node *nxt, *op;
}NODE;
NODE edg[MM]; // 保存所有的边
NODE *link[NN]; // 记录节点所在链表的首节点
int h[NN]; // 距离标号,记录每个点到汇点的距离,这里的距离指的是层数
int num[NN]; // gap优化,标号为i的顶点个数
int M, N, idx, S, T, n; // S 表示源点,T表示汇点,n表示节点个数
void add(int u, int v, int c){//很典型的加边法,不过是用链表存储的。
idx++;
edg[idx].v = v;
edg[idx].w = c;
edg[idx].nxt = link[u];
edg[idx].op = edg + idx + 1;
link[u] = edg + idx;
idx++;
edg[idx].v = u;
edg[idx].w = 0;
edg[idx].nxt = link[v];
edg[idx].op = edg + idx - 1;
link[v] = edg + idx;
}
int Min(int a, int b){
return a < b ? a : b;
}
int aug(int u, int flow){
if (u == T) return flow;
int l = flow; // l表示剩余容量
int tmp = n - 1;
for (NODE *p = link[u]; p; p = p->nxt){//遍历u点相关联的其它点
if (h[u] == h[p->v] + 1 && p->w){
int f = aug(p->v, Min(l, p->w));
l -= f;
p->w -= f;
p->op->w += f;
if (l == 0 || h[S] == n) return flow - l; // gap
}
// 这里是有剩余容量的可行边
if (p->w > 0 && h[p->v] < tmp){
tmp = h[p->v];
}
}
if(l == flow){// 如果没有找到增流,才修改标号,刚开始写错了,也杯具的过了好多题
num[h[u]]--; // gap
if (num[h[u]] == 0) h[S] = n; // gap,每个点的距离值最多为n - 1,这里设为n 表示断层了
else{
h[u] = tmp + 1;
num[h[u]]++; // gap
}
}
return flow - l;.//这里是L不是1.
}
/*n表示总点的个数,包括源点和汇点*/
void sap(){
int ans = 0;
memset(h, 0, sizeof(h)); // h 保存的是距离标号(到汇点的)
memset(num, 0, sizeof(num));
num[0] = n;
while(h[S] < n){
ans += aug(S, INF);
}
printf("%d\n", ans);
}
邻接表实现:
const int MAXN=20010;//点数的最大值
const int MAXM=880010;//边数的最大值
const int INF=0x3f3f3f3f;
struct Node
{
int from,to,next;
int cap;
}edge[MAXM];
int tol;
int head[MAXN];
int dep[MAXN];
int gap[MAXN];//gap[x]=y :说明残留网络中dep[i]==x的个数为y
int n;//n是总的点的个数,包括源点和汇点
void init()
{
tol=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w)
{
edge[tol].from=u;
edge[tol].to=v;
edge[tol].cap=w;
edge[tol].next=head[u];
head[u]=tol++;
edge[tol].from=v;
edge[tol].to=u;
edge[tol].cap=0;
edge[tol].next=head[v];
head[v]=tol++;
}
void BFS(int start,int end)
{
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
gap[0]=1;
int que[MAXN];
int front,rear;
front=rear=0;
dep[end]=0;
que[rear++]=end;
while(front!=rear)
{
int u=que[front++];
if(front==MAXN)front=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(dep[v]!=-1)continue;
que[rear++]=v;
if(rear==MAXN)rear=0;
dep[v]=dep[u]+1;
++gap[dep[v]];
}
}
}
int SAP(int start,int end)
{
int res=0;
BFS(start,end);
int cur[MAXN];
int S[MAXN];
int top=0;
memcpy(cur,head,sizeof(head));
int u=start;
int i;
while(dep[start]<n)
{
if(u==end)
{
int temp=INF;
int inser;
for(i=0;i<top;i++)
if(temp>edge[S[i]].cap)
{
temp=edge[S[i]].cap;
inser=i;
}
for(i=0;i<top;i++)
{
edge[S[i]].cap-=temp;
edge[S[i]^1].cap+=temp;
}
res+=temp;
top=inser;
u=edge[S[top]].from;
}
if(u!=end&&gap[dep[u]-1]==0)//出现断层,无增广路
break;
for(i=cur[u];i!=-1;i=edge[i].next)
if(edge[i].cap!=0&&dep[u]==dep[edge[i].to]+1)
break;
if(i!=-1)
{
cur[u]=i;
S[top++]=i;
u=edge[i].to;
}
else
{
int min=n;
for(i=head[u];i!=-1;i=edge[i].next)
{
if(edge[i].cap==0)continue;
if(min>dep[edge[i].to])
{
min=dep[edge[i].to];
cur[u]=i;
}
}
--gap[dep[u]];
dep[u]=min+1;
++gap[dep[u]];
if(u!=start)u=edge[S[--top]].from;
}
}
return res;
}