http://poj.org/problem?id=2195
题目大意:
给定一个N*M的地图,地图上有一定数量的m(代表人)和H(代表房子),两者数量相等;
要求求出把所有m移动到H的位置上总距离最小为多少;
思路:
用最优二分匹配或者最小费用最大流都能做,觉得用最优二分匹配会简单一些;
建图把m和H分别作为二分图的两个顶点集合vx和vy,求出两两之间的距离,用km算法求出最优匹配;
要注意的是,要求距离的最短,而km求的是最大匹配值,所以建图的时候距离要取反;
这是KM做的,费用流做的在下面;
#include<iostream>
#include<cmath>
using namespace std;
const int MAX=105;
const int inf=1<<30;
int n,lack;
int map[MAX][MAX];
char maps[MAX][MAX];
int dx[MAX],dy[MAX];
int link[MAX];
bool visx[MAX],visy[MAX];
bool DFS(int v)
{
visx[v]=true;
for(int i=0;i<n;i++)
{
if(visy[i])
continue;
int t=dx[v]+dy[i]-map[v][i];
if(!t)
{
visy[i]=true;
if(link[i]==-1||DFS(link[i]))
{
link[i]=v;
return true;
}
}
else
if(t<lack)
lack=t;
}
return false;
}
void KM()
{
int i,j;
memset(dx,0,sizeof(dx));
memset(dy,0,sizeof(dy));
memset(link,-1,sizeof(link));
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
if(map[i][j]>dx[i])
dx[i]=map[i][j];
}
for(i=0;i<n;i++)
{
while(true)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
lack=0x7fffffff;
if(DFS(i))
break;
for(j=0;j<n;j++)
{
if(visx[j])
dx[j]-=lack;
if(visy[j])
dy[j]+=lack;
}
}
}
}
int main(int i,int j,int k,int l)
{
int row,col,ans,numi,numj;
while(scanf("%d%d",&row,&col)&&(row+col))
{
/*Initial*/
n=ans=numi=numj=0;
memset(link,-1,sizeof(link));
memset(map,0,sizeof(map));
/*Input*/
for(i=0;i<row;i++)
{
scanf("%*c");
for(j=0;j<col;j++)
{
scanf("%c",&maps[i][j]);
if(maps[i][j]=='m')
n++;
}
}
/*Structure Graph*/
/*±ßȨֵ±äΪ¸ºÈ¨£¬Çó³ö×î´óÆ¥ÅäÖµ¼´Îª×îС¾àÀë×ܺÍ*/
for(i=0;i<row;i++)
{
for(j=0;j<col;j++)
{
if(maps[i][j]=='m')
{
for(k=0;k<row;k++)
{
for(l=0;l<col;l++)
{
if(maps[k][l]=='H')
map[numi][numj++]=-(abs(k-i)+abs(l-j)); //±ßΪ¸ºÈ¨Öµ
}
}
numi++;
numj=0;
}
}
}
KM();
int ans=0;
for(i=0;i<n;i++)
{
if(link[i]!=-1)
ans+=map[link[i]][i];
}
printf("%d\n",-ans);
}
return 0;
}
-------------------------------------------------华丽的分割线------------------------------------------
#include<iostream>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
const int inf=1<<30;
const int N=100500;
const int M=200500;
class Edge
{
public:
int u;
int v;
int cost;
int flow;
int next;
}edge[M<<3];
class Node
{
public:
int x;
int y;
}man[105],house[105];
int s,t;
int edge_num;
int max_flow;
int head[N];
int pre[N];
int dist[N];
bool vis[N];
char map[105][105];
void addedge(int u,int v,int cost,int flow)
{
edge[edge_num].u=u;
edge[edge_num].v=v;
edge[edge_num].flow=flow;
edge[edge_num].cost=cost;
edge[edge_num].next=head[u];
head[u]=edge_num++;
edge[edge_num].u=v;
edge[edge_num].v=u;
edge[edge_num].flow=0;
edge[edge_num].cost=-cost;
edge[edge_num].next=head[v];
head[v]=edge_num++;
return ;
}
/*SPFA 对费用求最短路*/
bool spfa()
{
queue <int> que;
memset(pre,-1,sizeof(pre));
memset(vis,false,sizeof(vis));
for(int j=0;j<=t;j++)
dist[j]=inf;
dist[s]=0;
vis[s]=true;
que.push(s);
while(!que.empty())
{
int x=que.front();
que.pop();
int y;
for(int i=head[x];~i;i=edge[i].next)
{
y=edge[i].v;
if(edge[i].flow && dist[y]>dist[x]+edge[i].cost)
{
dist[y]=dist[x]+edge[i].cost;
pre[y]=i;
if(!vis[y])
{
vis[y]=true;
que.push(y);
}
}
}
vis[x]=false;
}
if(dist[t]==inf) return false;
return true;
}
void argument_flow()
{
int v,alfa=inf;
for(v=pre[t];~v;v=pre[ edge[v].u ])
{
if(alfa>edge[v].flow)
alfa=edge[v].flow;
}
for(v=pre[t];~v;v=pre[ edge[v].u ])
{
edge[v].flow -= alfa;
edge[v^1].flow += alfa;
}
max_flow += alfa*dist[t];
}
int main(int i,int j)
{
int n,m;
while(~scanf("%d %d",&n,&m)&&n&&m)
{
edge_num=0;
max_flow=0;
s=0;
t=n*m+1;
memset(head,-1,sizeof(head));
int p=0;
int q=0;
/*Input & Structure Graph*/
for(i=1;i<=n;i++)
{
scanf("%s",map[i]+1);
for(j=1;j<=m;j++)
{
int x=(i-1)*m+j;
if(map[i][j]=='m')
{
addedge(s,x,0,1);
man[++p].x=i;//储存人的坐标
man[p].y=j;
}
if(map[i][j]=='H')
{
addedge(x,t,0,1);
house[++q].x=i;//储存房子的坐标
house[q].y=j;
}
}
}
for(i=1;i<=p;i++)
{
/*人和房子之间连边*/
for(j=1;j<=q;j++)
addedge(man[i].x*m+man[i].y-m,house[j].x*m+house[j].y-m,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y),1);
}
while(spfa())
argument_flow();
printf("%d\n",max_flow);
}
return 0;
}