poj2195Going Home(费用流或KM算法)
2016-09-12 19:15
375 查看
这道题目是比较简单的模板题,我用了两种方法写了一下,需要注意的是用KM算法写的时候,因为这个算法本身求得是二分图最大权匹配,而这道题求的是最小,那么应该在加边的时候把权值乘负一
费用流:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<stack> using namespace std; typedef long long ll; const int maxn=105; const int INF=0x3f3f3f3f; int nx,ny; int g[maxn][maxn]; int linker[maxn],lx[maxn],ly[maxn]; int slack[maxn]; bool visx[maxn],visy[maxn]; bool dfs(int x) { visx[x]=true; for(int y=0;y<ny;y++) { if(visy[y]) continue; int tmp=lx[x]+ly[y]-g[x][y]; if(tmp==0) { visy[y]=true; if(linker[y]==-1||dfs(linker[y])) { linker[y]=x; return true; } } else if(slack[y]>tmp) slack[y]=tmp; } return false; } int KM() { memset(linker,-1,sizeof(linker)); memset(ly,0,sizeof(ly)); for(int i=0;i<nx;i++) { lx[i]=-INF; for(int j=0;j<ny;j++) { if(g[i][j]>lx[i]) lx[i]=g[i][j]; } } for(int x=0;x<nx;x++) { for(int i=0;i<ny;i++) slack[i]=INF; while(true) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(dfs(x)) break; int d=INF; for(int i=0;i<ny;i++) if(!visy[i]&&d>slack[i]) d=slack[i]; for(int i=0;i<nx;i++) if(visx[i]) lx[i]-=d; for(int i=0;i<ny;i++) { if(visy[i]) ly[i]+=d; else slack[i]-=d; } } } int res=0; for(int i=0;i<ny;i++) if(linker[i]!=-1) res+=g[linker[i]][i]; return res; } char s[105][105]; struct sa { int x,y; }h[maxn],r[maxn]; int main() { int n,m; while(cin>>n>>m) { if(n==0&&m==0) break; memset(s,0,sizeof(s)); memset(g,0,sizeof(g)); int num1=0,num2=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cin>>s[i][j]; if(s[i][j]=='H') { h[num1].x=i; h[num1++].y=j; } else if(s[i][j]=='m') { r[num2].x=i; r[num2++].y=j; } } } for(int i=0;i<num1;i++) for(int j=0;j<num2;j++) { g[i][j]=(abs(h[i].x-r[j].x)+abs(h[i].y-r[j].y))*-1; // cout<<g[i][j]<<endl; } nx=ny=num1; // cout<<nx<<endl; cout<<KM()*-1<<endl; /* prepare(num1*2+2,0,2*num1+1); for(int i=1;i<=num1;i++) { addedge(0,i,1,0); addedge(i+num1,2*num1+1,1,0); } for(int i=1;i<=num1;i++) { for(int j=1;j<=num2;j++) { int sum=abs(h[i].x-r[j].x)+abs(h[i].y-r[j].y); addedge(i,j+num1,1,sum); } } cout<<spfaflow()<<endl;*/ } return 0; }
费用流:
#include <iostream> #include <string.h> #include <algorithm> using namespace std; const int maxm=66666;//边的最大数量,为原图的两倍 const int maxn=5555; const int oo=1e9; int src,dest,node,edge; int ver[maxm],cost[maxm],flow[maxm],next[maxm];//node节点数,src源点,dest汇点,edge边数 int head[maxn],dis[maxn],p[maxn],q[maxn]; //head链表头,p记录可行流上节点对应的反向边,dis计算距离 ,q在模拟队列 //int h[55][55]; bool vis[maxn]= {0}; void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0; i<node; ++i) head[i]=-1; edge=0; } void addedge(int u,int v,int f,int c)//ver表示边的指向,flow是边的容量,cost是边的费用,next是链表的下一条边 { ver[edge]=v,flow[edge]=f,cost[edge]=c,next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,cost[edge]=-c,next[edge]=head[v],head[v]=edge++; } bool spfa() { int i,u,v,l,r=0,tmp; for(i=0; i<node; ++i) dis[i]=oo; dis[q[r++]=src]=0; p[src]=p[dest]=-1; for(l=0; l!=r; (++l==maxn)?l=0:l) for(i=head[u=q[l]],vis[u]=0; i>=0; i=next[i]) if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i]))//u->v容量未饱和,且能够松弛 { dis[v]=tmp; p[v]=i^1; if(vis[v]) continue; vis[q[r++]=v]=1; if(r==maxn) r=0; } return p[dest]>-1; } int spfaflow() { int i,delta,ret=0; while(spfa()) { for(i=p[dest],delta=oo; i>=0; i=p[ver[i]]) if(flow[i^1]<delta) delta=flow[i^1];//可分配最大流 为增广链上的最小容量边的容量 for(i=p[dest]; i>=0; i=p[ver[i]]) { flow[i]+=delta;//反向弧容量加上可分配最大流 flow[i^1]-=delta;//正向弧容量减去可分配最大流 } ret+=delta*dis[dest]; } return ret; } char s[105][105]; struct sa { int x,y; }h[maxn],r[maxn]; int main() { int n,m; while(cin>>n>>m) { if(n==0&&m==0) break; memset(s,0,sizeof(s)); int num1=0,num2=0; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { cin>>s[i][j]; if(s[i][j]=='H') { h[++num1].x=i; h[num1].y=j; } else if(s[i][j]=='m') { r[++num2].x=i; r[num2].y=j; } } } prepare(num1*2+2,0,2*num1+1); for(int i=1;i<=num1;i++) { addedge(0,i,1,0); addedge(i+num1,2*num1+1,1,0); } for(int i=1;i<=num1;i++) { for(int j=1;j<=num2;j++) { int sum=abs(h[i].x-r[j].x)+abs(h[i].y-r[j].y); addedge(i,j+num1,1,sum); } } cout<<spfaflow()<<endl; } return 0; }
相关文章推荐
- POJ 2195 Going Home 费用流模版题(附KM算法,转)
- 二分图带权匹配 KM算法与费用流模型建立
- HDU 3435 KM算法或者最小费用最大流
- 二分图带权匹配 KM算法与费用流模型建立(转载)
- BZOJ 4819: [Sdoi2017]新生舞会 01分数规划 二分图最大权匹配(KM算法)/费用流
- 二分图带权匹配的KM算法以及费用流建模
- 【玲珑杯 1047】【二分匹配 KM算法或者费用流】Best couple【定义男女生的距离为最短距离,求匹配之后使得总距离最大】
- [BZOJ1937][SHOI2004]Mst最小生成树(KM算法,最大费用流)
- 二分图带权匹配 KM算法与费用流模型建立
- 带权二分图匹配:KM算法与费用流建模
- [对偶 KM算法 生成树 || 最大费用可行流 || 线性规划] BZOJ 1937 [Shoi2004]Mst 最小生成树
- 二分图带权匹配 KM算法与费用流模型建立
- 二分图带权匹配 KM算法与费用流模型建立
- HDU 2255(KM算法)
- DP之背包问题(01背包+完全背包+分组背包+多重背包+二维费用背包)
- 洛谷P4012 深海机器人问题(费用流)
- BZOJ3130 [Sdoi2013]费用流 【网络流 + 二分】
- [AHOI2014&&JSOI2014][bzoj3876] 支线剧情 [上下界费用流]
- BZOJ4819 [Sdoi2017]新生舞会 【01分数规划 + 费用流】
- 部分直播平台被整治,新风口选择的却是直播+短视频?开发费用?