您的位置:首页 > 编程语言 > Go语言

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  费用流 KM算法 图论