网络流改进SAP算法模版、HDU 1532 Drainage Ditches(解题报告)
2012-08-13 01:36 java环境变量 阅读(185) 评论(0) 编辑 收藏 举报转载请注明出处:http://write.blog.csdn.net/postlist
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1532
本次网络流算法看了两天了,先学习了EK算法,发现速度不够快,于是查找资料,得网络流诸多算法中主流算法(SAP算法)学习之。
相关链接:
1、网络流的算法分类:http://www.notonlysuccess.com/index.php/algorithm-of-network/
2、牛人博客,几种算法的分析:http://blog.csdn.net/abcjennifer/article/details/5556455
3、较详细的SAP算法分析:http://hi.baidu.com/hewei156/blog/item/f17038326b2f91c0a2cc2b78.html
今天看了挺久的HUD 3572 http://acm.hdu.edu.cn/showproblem.php?pid=1532 自己用下面的模版写,800多ms。 然后看了别人用邻接表存边时间在100以内! 遂学习之。
然后,然后重点来了! 我发现这个模版竟然可以去掉宽搜那部分!! 具体原因尚未确定。鉴于明天还要早起,先把代码贴在最后,明天再研究!
研究的一天多的代码模版(该模版正确性及可用性有待证明,以后若出现什么问题将进行修改):
#include<iostream> #include<algorithm> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<queue> #include<math.h> #define maxn 205 #define INF 100000000 using namespace std; int n,m,s,t;// n点数,m边数,s起点,t终点 int map[maxn][maxn],fa[maxn],gap[maxn],level[maxn];//map有向图,fa父节点, //gap[i]=j表示以汇点距离为i的点有j个,level[i]=j表示点i与汇点距离为j void sap_bfs()//反向 BFS 初始化所有顶点的距离标号、初始化level和gap数组 { memset(level,1,sizeof(level));//这个很神奇,每个int的4字节都变成0000 0001 //即数组被初始化为二进制的00000001 00000001 00000001 00000001 ,十进制为16843009 memset(gap,0,sizeof(gap)); queue<int> q; q.push(t); level[t]=0;//汇点距离为0 gap[level[t]]++; while(!q.empty()) { int temp=q.front(); q.pop(); for(int i=1;i<=n;i++) if(level[i]>n && map[i][temp]>0) { q.push(i); level[i]=level[temp]+1; gap[level[i]]++; } } } int fine_path(int u)//查找残量网络中是否有与u相连 且距离为1的点 { for(int i=1;i<=n;i++) if(map[u][i]>0 && level[u]==level[i]+1) return i; return -1; } int Advance()//找到汇点时进行增广 { int i,minflow=INF; for(i=t;i!=s;i=fa[i]) if(minflow>map[fa[i]][i])//查找这条增广路中最小的流量 { minflow=map[fa[i]][i]; } for(i=t;i!=s;i=fa[i]) { map[fa[i]][i]-=minflow; map[i][fa[i]]+=minflow;//反向边也要操作 } return minflow; } int retreat(int u)//对点u重新设置最小的距离 { int fine=INF; for(int i=1;i<=n;i++) if(map[u][i]>0 && fine>level[i]+1) fine=level[i]+1; if(fine==INF) fine=n;//找不到的话设置为最大,即点u不会再被经过 return fine; } int cxbsap() { sap_bfs(); int i,j,v,u=s,flow=0;//初始化 while(level[s]<=n)//源点与汇点距离小于n时执行,要是不存在源点到汇点的通路直接跳出 { v=fine_path(u);// 找u的下一个点 if(v>0) { fa[v]=u;//找到后标记父节点 u=v;//把v赋值给u,如果不是汇点,直接跳回75行了 if(u==t) { flow+=Advance(); u=s;//找的一条路径后重新查找 } } else { if(--gap[level[u]]==0) return flow;//如果其中一个距离个数为0,即出现断层,没有通路看,直接跳出 v=retreat(u); gap[v]++; level[u]=v; if(u!=s) u=fa[u]; } } return flow; } int main() { // freopen("in.txt","r",stdin); int i,j,a,b,x; while(~scanf("%d%d",&m,&n)) { memset(map,0,sizeof(map)); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&x); map[a][b]+=x; } s=1; t=n; int ans=cxbsap(); printf("%d\n",ans); } return 0; }
我发现其实自己有点强迫症,我喜欢带参数的函数,所以:
/* 网络流改进sap模版:HDU1532 计算一个点编号1~n的网络的最大流; 时间复杂度o(v*e^2) v点数,e边数 陈小宾,2012、8、13凌晨 */ #include<iostream> #include<algorithm> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<queue> #include<math.h> #define maxn 205 #define INF 100000000 using namespace std; int n,m;// n点数,m边数,s起点,t终点 int map[maxn][maxn],fa[maxn],gap[maxn],level[maxn];//map有向图,fa父节点, //gap[i]=j表示以汇点距离为i的点有j个,level[i]=j表示点i与汇点距离为j void sap_bfs(int t)//反向 BFS 初始化所有顶点的距离标号、初始化level和gap数组 { memset(level,1,sizeof(level));//这个很神奇,每个int的4字节都变成0000 0001 //即数组被初始化为二进制的00000001 00000001 00000001 00000001 ,十进制为16843009 memset(gap,0,sizeof(gap)); queue<int> q; q.push(t); level[t]=0;//汇点距离为0 gap[level[t]]++; while(!q.empty()) { int temp=q.front(); q.pop(); for(int i=1;i<=n;i++) if(level[i]>n && map[i][temp]>0) { q.push(i); level[i]=level[temp]+1; gap[level[i]]++; } } } int fine_path(int u)//查找残量网络中是否有与u相连 且距离为1的点 { for(int i=1;i<=n;i++) if(map[u][i]>0 && level[u]==level[i]+1) return i; return -1; } int Advance(int s,int t)//找到汇点时进行增广 { int i,minflow=INF; for(i=t;i!=s;i=fa[i]) if(minflow>map[fa[i]][i])//查找这条增广路中最小的流量 { minflow=map[fa[i]][i]; } for(i=t;i!=s;i=fa[i]) { map[fa[i]][i]-=minflow; map[i][fa[i]]+=minflow;//反向边也要操作 } return minflow; } int retreat(int u)//对点u重新设置最小的距离 { int fine=INF; for(int i=1;i<=n;i++) if(map[u][i]>0 && fine>level[i]+1) fine=level[i]+1; if(fine==INF) fine=n;//找不到的话设置为最大,即点u不会再被经过 return fine; } int cxbsap(int s,int t) { sap_bfs(t); int i,j,v,u=s,flow=0;//初始化 while(level[s]<=n)//源点与汇点距离小于n时执行,要是不存在源点到汇点的通路直接跳出 { v=fine_path(u);// 找u的下一个点 if(v>0) { fa[v]=u;//找到后标记父节点 u=v;//把v赋值给u,如果不是汇点,直接跳回75行了 if(u==t) { flow+=Advance(s,t); u=s;//找的一条路径后重新查找 } } else { if(--gap[level[u]]==0) return flow;//如果其中一个距离个数为0,即出现断层,没有通路看,直接跳出 v=retreat(u); gap[v]++; level[u]=v; if(u!=s) u=fa[u]; } } return flow; } int main() { // freopen("in.txt","r",stdin); int i,j,a,b,x; while(~scanf("%d%d",&m,&n)) { memset(map,0,sizeof(map)); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&x); map[a][b]+=x; } int ans=cxbsap(1,n); printf("%d\n",ans); } return 0; }
ok,此文先到此为止,还要挂题目,明天早起!! 睡觉,cxb,晚安,加油!
不用宽搜的代码,只要初始化两个数组就可以了:
#include<iostream> #include<algorithm> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<queue> #include<math.h> #define maxn 205 #define INF 100000000 using namespace std; int n,m,s,t,ans;// n点数,m边数,s起点,t终点 int map[maxn][maxn],fa[maxn],gap[maxn],level[maxn]; int fine_path(int u) { for(int i=1;i<=n;i++) if(map[u][i]>0 && level[u]==level[i]+1) return i; return -1; } int Advance() { int i,minflow=INF; for(i=t;i!=s;i=fa[i]) if(minflow>map[fa[i]][i]) { minflow=map[fa[i]][i]; } for(i=t;i!=s;i=fa[i]) { map[fa[i]][i]-=minflow; map[i][fa[i]]+=minflow; } return minflow; } int retreat(int u) { int fine=INF; for(int i=1;i<=n;i++) if(map[u][i]>0 && fine>level[i]+1) fine=level[i]+1; if(fine==INF) fine=n; return fine; } int cxbsap() { int i,j,v,u=s,flow=0; while(level[s]<=n) { v=fine_path(u); if(v>0) { fa[v]=u; u=v; if(u==t) { flow+=Advance(); u=s; } } else { if(--gap[level[u]]==0) return flow; v=retreat(u); gap[v]++; level[u]=v; if(u!=s) u=fa[u]; } } return flow; } int main() { // freopen("in.txt","r",stdin); int i,j,a,b,x; while(~scanf("%d%d",&m,&n)) { memset(map,0,sizeof(map)); memset(level,0,sizeof(level)); memset(gap,0,sizeof(gap)); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&x); map[a][b]+=x; } s=1; t=n; ans=cxbsap(); printf("%d\n",ans); } return 0; }