题意:一个100*100的图上有n个房子n个人(n<=100),每个人到房子的距离为笛卡尔坐标,且不受障碍影响,问每个人回到一个单独的家所需要的最小费用
建立一个超级源点和超级汇点,每条边都是流量为1,这样可以保证在最大流的时候每个人都匹配一个房子,之后为每个人和每个房子之间赋上所需的费用,跑一边费用流板子就可以了。
套板子所需要注意的细节有起始点和终点是0和t1*2+1,还有费用是每条边一个费用而不是每个流量一个费用,需要改一下板子。
#include <cmath> //定义数学函数
#include <cstdio> //定义输入/输出函数
#include <iostream>
#include <queue> //STL 队列容器
#define x first
#define y second
using namespace std;
typedef pair<int,int>pii;
const int maxn = 1e3 + 10;
const int maxm = 1e5 + 10;
const int inf = 0x3f3f3f3f;
struct Edg
{
int to,flow,cost,nxt;//下个点、流量、费用、前项星
}edg[maxm];//从0开始存边
int edg_cnt,head[maxn];//从1开始存点
void addedg(int a,int b,int c,int d)
{
edg[edg_cnt]={b,c,d,head[a]};
head[a]=edg_cnt++;
edg[edg_cnt]={a,0,-d,head[b]};
head[b]=edg_cnt++;
}
int n,m,s,t;
int t1,t2;
int pre[maxn],dis[maxn];
bool vis[maxn];
bool spfa()
{
for(int i=s;i<=t;i++)
{
dis[i]=inf;
vis[i]=0;
pre[i]=-1;
}
dis[s]=0;
queue<int>q;
q.push(s);vis[s]=1;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];~i;i=edg[i].nxt)
{
int v=edg[i].to;
if(edg[i].flow&&dis[v]>dis[u]+edg[i].cost)
{
dis[v]=dis[u]+edg[i].cost;
pre[v]=i;
if(!vis[v])
{
q.push(v);vis[v]=1;
}
}
}
}
return pre[t]!=-1;
}
void costflow()
{
int cost=0;
while(spfa())
{
int mi=inf;
for(int i=pre[t];~i;i=pre[edg[i^1].to])
{
mi=min(mi,edg[i].flow);
cost+=edg[i].cost;
}
for(int i=pre[t];~i;i=pre[edg[i^1].to])
{
edg[i].flow-=mi;
edg[i^1].flow+=mi;
}
}
printf("%d\n",cost);
}
string str[110];
pii hs[110],man[110];
int main()
{
// freopen("in.txt","r",stdin);
while(1)
{
scanf("%d%d",&n,&m);
if(!n&&!m)break;
t1=0,t2=0,edg_cnt=0;
for(int i=0;i<n;i++)
{
cin>>str[i];
for(int j=0;j<m;j++)
{
if(str[i][j]=='H')hs[++t1]={i,j};
else if(str[i][j]=='m')man[++t2]={i,j};
}
}
s=0,t=t1*2+1;
for(int i=s;i<=t;i++)head[i]=-1;
for(int i=1;i<=t1;i++)
{
addedg(0,i,1,0);
addedg(i+t1,t,1,0);
}
for(int i=1;i<=t1;i++)
{
for(int j=1;j<=t1;j++)
{
addedg(i,j+t1,1,abs(hs[i].x-man[j].x)+abs(hs[i].y-man[j].y));
}
}
costflow();
}
return 0;
}