题目链接:点击这里
解题思路:
首先要知道怎么写最大流,不懂的可以先转个场:浅谈网络流
那么最小费用最大流就是现在每个边不只有容量了,还有一个花费,就是单位流量流过要的花费。
要求你求出在最大流的情况下的最小花费,所以之前我们再求最大流不考虑花费的情况下,只要能达到最大流就可以了,并不用去考虑流量是走哪些边的,现在就是要考虑了。
那么在建边的时候正向边的花费就是我们求得的值,反向边就是相应的负值,因为当"反悔"的时候这条边是不走了,要把之前的花费还回去。
这题我们还需要构造一个超级源点和超级汇点,源点和人构造连接,房子和汇点构造连接就构成了一个网络流.初始化所有边的容量都是1,我们构造的连接的花费就是0了,原来的边多少花费就是多少,
求解步骤:
1.先用最短路算法(spfa,dij..想用啥用啥)在图上跑出源点到汇点的最短路径(对于花费来说的),当然边的限制跟增广路一样必须路径上的每条边残量都大于0.
2.求出这条路径的最大增大流量(mins),然后用dis[T]*mins,dis[T]就是到汇点的最短距离.这个就是当前最优增广路的花费。
3.最短路径的增广路也要跟网络流的操作一样,残量网络也要改变,即正向边-=扩增流量,反向边+=扩增流量。
4.重复上面1-3操作,直到没有最短路径的增广路,最后答案就是增广得到的花费和.
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int mx = 4e2 + 10;
typedef long long ll;
int n,m,head[mx],tot,hi[mx][2],mi[mx][2];
char str[mx][mx];
struct node
{
int y,nxt;
int v,c;
}edge[mx<<5];
int S,T,dis[mx],pre[mx];
bool vis[mx];
void AddEdge(int x,int y,int v,int c)
{
edge[tot] = {y,head[x],v,c};
head[x] = tot++;
}
bool spfa()
{
memset(dis,inf,sizeof(dis));
memset(pre,-1,sizeof(pre));
queue<int> que;
que.push(S);
dis[S] = 0;
while(!que.empty()){
int no = que.front();
que.pop();
vis[no] = 0;
for(int i=head[no];~i;i=edge[i].nxt)
{
int y = edge[i].y;
if(edge[i].v&&dis[y]>dis[no]+edge[i].c){
dis[y] = dis[no] + edge[i].c;
pre[y] = i;
if(!vis[y]){
que.push(y);
vis[y] = 1;
}
}
}
}
return dis[T] != inf;
}
int mincost()
{
int ans = 0;
while(spfa()){
int mins = inf;
for(int i=pre[T];~i;i=pre[edge[i^1].y]) mins = min(mins,edge[i].v);
for(int i=pre[T];~i;i=pre[edge[i^1].y])
{
edge[i].v -= mins;
edge[i^1].v += mins;
}
ans += mins*dis[T];
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m)&&(n+m))
{
memset(head,-1,sizeof(head));
tot = 0;
for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
int N = 0,M = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(str[i][j]=='m'){
mi[++N][0] = i;
mi[N][1] = j;
}if(str[i][j]=='H'){
hi[++M][0] = i;
hi[M][1] = j;
}
}
}
S = 0, T = 2*N + 1;
for(int i=1;i<=N;i++){
AddEdge(S,i,1,0);
AddEdge(i,S,0,0);
AddEdge(i+N,T,1,0);
AddEdge(T,i+N,0,0);
for(int j=1;j<=N;j++){
AddEdge(i,j+N,1,abs(mi[i][0]-hi[j][0])+abs(mi[i][1]-hi[j][1]));
AddEdge(j+N,i,0,-abs(mi[i][0]-hi[j][0])-abs(mi[i][1]-hi[j][1]));
}
}
printf("%d\n",mincost());
}
return 0;
}