转载请注明出处:http://blog.csdn.net/sprintfwater/article/details/7913181
Dinic算法
Accepted 1532 15MS 280K 2468 B C++ SpringWater
可能是数据比较水吧,居然和sap算法一个时间效率,这个算法算是最差的了。当然相对与DFS来说还是要强得多
这个算法的基本思想:每次求可达路径都根据刚分好层的图,从src一层层的递增找到des,之后有一次分层,
又深搜。没有gap优化,cur优化,和没有利用insert来避免每次都分层(即当前弧优化)。
总结者:SpringWater(GHQ)
#include<stdio.h>
#include<string.h>
#define mafromn 222
#define MAXN 222
#define MAXE 500000
const int inf = 0x3f3f3f3f;
struct edge
{
int from,to,next;
int c;
}edge[MAXE];
int cnt,head[MAXN],stack[MAXN],dep[MAXN];
void add(int from,int to,int c)
{
edge[cnt].from=from;edge[cnt].to=to;edge[cnt].c=c;
edge[cnt].next=head[from];head[from]=cnt++;
edge[cnt].from=to;edge[cnt].to=from;edge[cnt].c=0;
edge[cnt].next=head[to];head[to]=cnt++;
}
int flow(int n,int src,int des)
{
int tr,res=0;
int i,j,k,f,r,top;
while(1){
memset(dep,-1,n*sizeof(int));
for(f=dep[stack[0]=src]=0,r=1;f!=r;)
{
for(i=stack[f++],j=head[i];j!=-1;j=edge[j].next)
{
if(edge[j].c&&-1==dep[k=edge[j].to]){
dep[k]=dep[i]+1;stack[r++]=k;
if(k==des)
{
f=r;
break;
}
}
}
}
if(dep[des]==-1)break;
for(i=src,top=0;;)
{
if(i==des)
{
for(k=0,tr=inf;k<top;++k)
if(edge[stack[k]].c<tr)tr=edge[stack[f=k]].c;
for(k=0;k<top;++k)
edge[stack[k]].c-=tr,edge[stack[k]^1].c+=tr;
res+=tr;i=edge[stack[top=f]].from;
}
for(j=head[i];j!=-1;j=edge[j].next)
if(edge[j].c&&dep[i]+1==dep[edge[j].to])break;
if(j!=-1)
{
stack[top++]=j;
i=edge[j].to;
}
else
{
if(!top)break;
dep[i]=-1;i=edge[stack[--top]].from;
}
}
}
return res;
}
int main()
{
int i;
int n, m, src, des;
while (~scanf("%d%d", &m, &n))
{
src = 0;//源点为0
des = n - 1;//汇点为n-1
cnt = 0;
memset(head, -1, sizeof(head));
int u, v, c;
for (i =1; i <= m; ++i)
{
scanf("%d%d%d", &u, &v, &c);
--u, --v;
add(u,v,c);
//add(v,u,c); //流量为双向,如果是单向得去掉这句
}
int ans = flow(n, src, des);
printf("%d\n", ans);
}
return 0;
}
SAP和ISAP算法的效率差不多:
总结者:SpringWater(GHQ)
#include <stdio.h>
#include <string.h>
#define MAXN 100010
#define MAXE 400010
const int INF = 0x3f3f3f3f;
struct Edge
{
int to, from, next, cap;
}edge[MAXE];
int head[MAXN],cnt,n,m,src,des;
int dep[MAXN], gap[MAXN]; //dep[des] = 0初始化为0,代表汇点des的深度为0,gap[0] = 1代表深度为0的点有1个,即汇点
void addedge(int cu, int cv, int cw)
{
edge[cnt].from = cu;
edge[cnt].to = cv;
edge[cnt].cap = cw;
edge[cnt].next = head[cu];
head[cu] = cnt++;
edge[cnt].from = cv;
edge[cnt].to = cu;
edge[cnt].cap = 0;
edge[cnt].next = head[cv];
head[cv] = cnt++;
}
int que[MAXN];
void BFS()
{
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof(gap));
gap[0] = 1;
int L = 0, R = 0;
dep[des] = 0;
que[R++] = des;
int u, v;
while (L != R) //用光搜的方法,从汇点开始,将此图分层。
{
u = que[L++];
L = L%MAXN;
for (int i=head[u]; i!=-1; i=edge[i].next)
{
v = edge[i].to;
if (edge[i ^1].cap == 0 || dep[v] != -1) //i^1代表i的反向边,edge[i ^1].cap == 0代表v点到u点不可答
continue;
que[R++] = v; //如果v点可达,则加入队列
R = R % MAXN;
++gap[dep[v] = dep[u] + 1]; //v的深度,等于u的深度+1,且深度为dep[v]的点的个数加1
}
}
}
int cur[MAXN],stack[MAXN];
int Sap() //sap模板
{
int res = 0;
BFS();
int top = 0;
memcpy(cur, head, sizeof(head)); //将每个点的第一条边复制给cur,以便于后面找深度u点小1的边不用从头开始找
int u = src, i;
while (dep[src] < n) //如果src找不到一个可达点,则dep[src] = n + 1自动退出
{
if (u == des) //当找到汇点
{
int temp = INF, inser = n;
for (i=0; i!=top; ++i){ //找从src点到des点的所有边中的最容量为temp,而那条边的编号为insert
if (temp > edge[stack[i]].cap){
temp = edge[stack[i]].cap;
inser = i;
}
}
for (i=0; i!=top; ++i) //将正向边-temp,反向变+temp
{
edge[stack[i]].cap -= temp;
edge[stack[i]^1].cap += temp;
}
res += temp; //总的流量加temp
top = inser; //stack只保留从src到最小边insert“前向点”之间的边的信息,即insert边以后的边信息都不要
u = edge[stack[top]].from; //u为insert的”前向点“
}
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; //优化下次找dep[u] - 1不用从头开始找了
stack[top++] = i;
u = edge[i].to;
}
else //当没有找到深度为dep[u] - 1的可达边,那只能找深度更大的边
{
/*因为在不断的增广过程中,每个点的dep只会增,不会减,深度比u小1的点已经没有了,
即深度大于等于dep[u]的点过不了,dep[u] - 1这层,而ISAP算法就不能这样优化,因为
它是将所有的点初始化为0,所以大于0的 而这道题把这句话扔掉反而时间还边少了0ms;
不过令人奇怪的是,这题在在去掉这个优化的时候时间反而还从0ms变为了15ms*/
if (--gap[dep[u]] == 0)
break;
int minn = n;
//从头开始找出深度最小的可达边
for (i = head[u]; i != -1; i = edge[i].next)
{
if (edge[i].cap == 0)
continue;
if (minn > dep[edge[i].to])
{
minn = dep[edge[i].to];
cur[u] = i; //优化避免从头找
}
}
++gap[dep[u] = minn + 1];
if (u != src) //如果u不是源点,还得回溯到dep[u] + 1(这里的dep[u]!=minn + 1)层,并将dep[u]层点全部修改变大。一直回溯到源点
u = edge[stack[--top]].from;
}
}
return res;
}
int main()
{
while (~scanf("%d%d", &m, &n))
{
src = 0;//源点为0
des = n - 1;//汇点为n-1
cnt = 0;
memset(head, -1, sizeof(head));
int u, v, c;
for (int i =0; i < m; ++i)
{
scanf("%d%d%d", &u, &v, &c);
--u, --v;//输入是从1点开始,而src是从0点开始
addedge(u,v,c);
// addedge(v,u,c); //流量为双向,如果是单向得去掉这句
}
int ans = Sap();
printf("%d\n", ans);
}
return 0;
}
//题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1532
ISAP算法:
Accepted 1532 0MS 444K 3014 B C++ SpringWater
初步实验这个算法最优:基本思想是将所有点的深度都初始化为0,在增广的过程中就自动分层。
再直接利用SAP算法增广即可,相对与SAP算法相比,只是代码简短一些,其他也占不了什么优势。
总结者:SpringWater(GHQ)
ps:这个代码是我自己敲的。终于敲网络流有种轻车熟路的感觉。
#include<iostream>
#include<stdio.h>
#include<memory.h>
#include<cmath>
using namespace std;
#define MAXN 10005
#define MAXE 200000
#define INF 1e9
int tmp,src,des,cnt;
int n,m;
struct Edge{
int from, to;
int next,cap;
}edge[MAXE];
int head[MAXN];
int gap[MAXN],dep[MAXN],cur[MAXN], stack[MAXN], top;
int ISAP()
{
int cur_fLow,max_fLow,u,insert,i;
memset(dep,0,sizeof(dep));
memset(gap,0,sizeof(gap));
memcpy(cur, head, n);
max_fLow = 0;
u = src;
top = 0;
while(dep[src] < n)
{
if(u == des)
{
cur_fLow = INF;
for(i = 0; i < top; ++i)
{
if(cur_fLow > edge[stack[i]].cap)
{
insert = i;
cur_fLow = edge[stack[i]].cap;
}
}
for(i = 0; i < top; ++i)
{
edge[stack[i]].cap -= cur_fLow;
edge[stack[i]^1].cap += cur_fLow;
}
max_fLow += cur_fLow;
u = edge[ stack[top = insert]].from;
}
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)
{
stack[top++] = i;
u = edge[i].to;
}
else
{
if(0 == --gap[dep[u]]) break;
int minn = n;
for(i = head[u]; i != -1; i = edge[i].next)
{
if(edge[i].cap > 0)
minn = (minn > dep[edge[i].to]) ? (cur[u]= i, dep[edge[i].to]) : minn;
}
++gap[dep[u] = minn + 1];
if(u != src) u = edge[stack[--top]].from;
}
}
return max_fLow;
}
void addedge(int u,int v,int f)
{
edge[cnt].next = head[u];
edge[cnt].from = u;
edge[cnt].to = v;
edge[cnt].cap = f;
head[u] = cnt++;
edge[cnt].next = head[v];
edge[cnt].from = v;
edge[cnt].to = u;
edge[cnt].cap = 0;
head[v] = cnt++;
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
cnt=0;
src = 0;
des = n-1;
memset(head,-1,sizeof(head));
while(m--)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
--a, --b;
addedge(a, b, c);
//addedge(b, a, c);如果是无向边的加上这句
}
int ans=ISAP();
printf("%d\n",ans);
}
return 0;
}