这道题是费用流模板题
处理费用流可以用edmonds-karp+spfa算法,这样可以有效的处理负边,但spfa的复杂度不够稳定
我们可以导入势的概念从而使用dijkstra+edmonds-karp来解决费用流
设顶点i的势为h(i),我们将图中原来的边d(e)变成d'(e)=d(e)+h(u)-h(v),这样改动后的图最终的答案只要减去h(s)-h(t)就是原图的答案
所以只要合理的选取势,就可以使得所有的d'(e)都是非负数,从而可以使用djikstra()
对于h(i),可以取dist[i]作为势的值,因为在djikstra算法中有
dist[v]<=dist[u]+d(e),e是连接u和v的边
如果刚开始有负边,就先跑一趟spfa,否则可以直接跑dijkstra (注意反向边中的负边不算,因为刚开始反向边没有流量不会走到)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;
const int MOD=1e9+7;
const int INF=0x7ffffff;
const int magic=348;
int prevv[100048],pree[100048];
int t=0,head[100048],to[200048],nxt[200048],f[200048],w[200048],tot=1;
void addedge(int s,int t,int cap,int cost)
{
to[++tot]=t;nxt[tot]=head[s];head[s]=tot;f[tot]=cap;w[tot]=cost;
to[++tot]=s;nxt[tot]=head[t];head[t]=tot;f[tot]=0;w[tot]=-cost;
}
int n,m,cnt;
char a[148][148];
vector<Pair> people,house;
priority_queue<Pair> q;
int dist[100048],h[100048];
inline int myabs(int x)
{
return x>=0?x:-x;
}
inline int calc(Pair x,Pair y)
{
int x1=x.x,y1=x.y,x2=y.x,y2=y.y;
return myabs(x1-x2)+myabs(y1-y2);
}
bool dijkstra()
{
int i,x,y,dd;
q.push(mp(0,0));
for (i=0;i<=t;i++) dist[i]=INF;
dist[0]=0;
while (!q.empty())
{
dd=q.top().x;x=q.top().y;
dd=-dd;q.pop();
if (dd>dist[x]) continue;
for (i=head[x];i;i=nxt[i])
{
y=to[i];
if (f[i] && dist[y]>dist[x]+w[i]+h[x]-h[y])
{
dist[y]=dist[x]+w[i]+h[x]-h[y];
prevv[y]=x;
pree[y]=i;
q.push(mp(-dist[y],y));
}
}
}
if (dist[t]>=INF) return false; else return true;
}
int min_cost_flow()
{
int i,x,y,u,res,minf;
for (i=0;i<=t;i++) h[i]+=dist[i];
minf=INF;
for (u=t;u;u=prevv[u])
minf=min(minf,f[pree[u]]);
res=minf*h[t];
for (u=t;u;u=prevv[u])
{
f[pree[u]]-=minf;
f[pree[u]^1]+=minf;
}
return res;
}
int main ()
{
int i,j;
while (scanf("%d%d",&n,&m) && n && m)
{
for (i=1;i<=n;i++) scanf("%s",a[i]+1);
people.clear();house.clear();t=0;tot=1;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
{
if (a[i][j]!='.') t++;
if (a[i][j]=='m') people.pb(mp(i,j));
if (a[i][j]=='H') house.pb(mp(i,j));
}
cnt=t/2;
t++;
for (i=0;i<=t;i++) head[i]=0;
for (i=0;i<=t;i++) h[i]=0;
for (i=1;i<=cnt;i++) addedge(0,i,1,0);
for (i=1;i<=cnt;i++) addedge(cnt+i,t,1,0);
for (i=0;i<people.size();i++)
for (j=0;j<house.size();j++)
{
int d=calc(people[i],house[j]);
addedge(i+1,cnt+j+1,1,d);
}
int ans=0;
while (dijkstra()) ans+=min_cost_flow();
printf("%d\n",ans);
}
return 0;
}