Going Home
题目:
给出一个N*M的图,图上的m表示人,H表示房子,每座房子只能有一个人,要求你所有人到房子中总步数最少。m个数与H个数一样多。
算法分析:
这个题目还是比较裸的。可以想到先求出每个人到每座房子的距离。然后求出最小花费,这个好像就是最小费用流吧?一开始用了KM写完后,发现。。。。哪里不对啊?后来才觉悟,原来题目是求解最小花费,KM是最大匹配下最大权值。。。。。
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <cstdio>
#include <cstring>
using namespace std;
typedef pair<int,int> P;
const int INF = 1 << 20;
const int MAXN = 400 + 10;
/
//最小费用
struct Edge{
int to,cap,cost,rev;
Edge(){};
Edge(int _to,int _cap,int _cost,int _rev)
:to(_to),cap(_cap),cost(_cost),rev(_rev){};
};
vector<Edge> G[MAXN];
int V;
int h[MAXN];
int dist[MAXN];
int prevv[MAXN],preve[MAXN];
int src,sink;
///
//查找距离
struct Point{
int x,y,dis;
Point(){};
Point(int _x,int _y,int _dis)
:x(_x),y(_y),dis(_dis){};
};
vector<Point> cost;
map<int,int> mp;
int N,M,cnt1,cnt2;
char str[MAXN][MAXN];
int dirx[5] = {-1,1,0,0};
int diry[5] = {0,0,-1,1};
int W[MAXN][MAXN],ans[MAXN];
int slack,Lx[MAXN],Ly[MAXN],Link[MAXN];
bool S[MAXN],T[MAXN];
bool check(int x,int y){
if(x >= 0&& x < N&&y >=0 && y < M)
return true;
return false;
}
void bfs(int s,int e){
bool vst[MAXN][MAXN];
memset(vst,0,sizeof(vst));
queue <Point> que;
que.push(Point(s,e,0));
vst[s][e] = 1;
while(!que.empty()){
Point pv = que.front(); que.pop();
for(int i = 0;i < 4;++i){
Point tmp;
tmp.x = pv.x + dirx[i];
tmp.y = pv.y + diry[i];
tmp.dis = pv.dis + 1;
if(check(tmp.x,tmp.y)&&!vst[tmp.x][tmp.y]){
vst[tmp.x][tmp.y] = 1;
if(str[tmp.x][tmp.y] == 'H'){
int num = tmp.x * N + tmp.y;
if(mp.count(num) == 0){
mp[num] = ++cnt2;
}
cost.push_back(Point(cnt1,mp[num],tmp.dis));
}
que.push(tmp);
}
}
}
}
//
//bool Match(int i){
// S[i] = true;
// for(int j = 1;j <= cnt2;++j)if(!T[j]){
// int tmp = Lx[i] + Ly[j] - W[i][j];
// if(tmp == 0){
// T[j] = true;
// if(Link[j] == -1||Match(Link[j])){
// Link[j] = i;
// return true;
// }
// }
// else if(tmp < slack)
// slack = tmp;
// }
// return false;
//}
//
//void update(int d){
// for(int i = 1;i <= cnt1;++i)
// if(S[i]) Lx[i] -= d;
// for(int i = 1;i <= cnt2;++i)
// if(T[i]) Ly[i] += d;
//}
//
//bool EK(){
// for(int i = 1;i <= cnt1;++i){
// Link[i] = -1;
// Lx[i] = Ly[i] = 0;
// for(int j = 1;j <= cnt2;++j)
// Lx[i] = max(Lx[i],W[i][j]);
// }
//
// slack = INF;
//
// for(int i = 1;i <= cnt1;++i){
// for(;;){
// memset(S,false,sizeof(S));
// memset(T,false,sizeof(T));
// if(Match(i))
// break;
//
// if(slack == INF)
// return false;
// update(slack);
// }
//
// }
// return true;
//}
//
//int getAns(){
// int sum = 0;
// for(int j = 1;j <= cnt2;++j){
// if(Link[j] != -1)
// sum += W[Link[j]][j];
// }
// return sum;
//}
void addEdge(int from,int to,int cap,int cost){
G[from].push_back(Edge(to,cap,cost,G[to].size()));
G[to].push_back(Edge(from,0,-cost,G[from].size() - 1));
}
int min_cost_flow(int s,int t,int f){
int res = 0;
V = sink + 1;
fill(h,h + V,0);
while(f > 0){
priority_queue<P,vector<P>,greater<P> > que;
fill(dist,dist + V,INF);
dist[s] = 0;
que.push(P(0,s));
while(!que.empty()){
P p = que.top(); que.pop();
int v = p.second;
if(dist[v] < p.first) continue;
for(int i = 0;i < (int)G[v].size();++i){
Edge& e = G[v][i];
int tmp = dist[v] + e.cost + h[v] - h[e.to];
if(e.cap > 0 && dist[e.to] > tmp){
dist[e.to] = tmp;
prevv[e.to] = v;
preve[e.to] = i;
que.push(P(dist[e.to],e.to));
}
}
}
if(dist[t] == INF){
return -1;
}
for(int v = 1;v < V;++v) h[v] += dist[v];
int dd = f;
for(int v = t;v != s ;v = prevv[v]){
dd = min(dd,G[prevv[v]][preve[v]].cap);
}
f -= dd;
res += dd * h[t];
for(int v = t;v != s;v = prevv[v]){
Edge& e = G[prevv[v]][preve[v]];
e.cap -= dd;
G[v][e.rev].cap += dd;
}
}
return res;
}
void init(){
for(int i = 0;i <= sink;++i)
G[i].clear();
}
void solve(){
cnt1 = 0;
cnt2 = 0;
mp.clear();
cost.clear();
//查找距离
for(int i = 0;i < N;++i){
for(int j = 0;j < M;++j){
if(str[i][j] == 'm'){
cnt1++;
bfs(i,j);
}
}
}
Point tmp;
for(int i = 0;i < (int)cost.size();++i){
tmp = cost[i];
W[tmp.x][tmp.y] = tmp.dis;
}
//建图
V = cnt1 + cnt2;
src = V + 1; sink = src + 1;
init();
for(int i = 1;i <= cnt1;++i){ //m
for(int j = 1;j <= cnt2;++j){ //H
addEdge(i,j + cnt1,1,W[i][j]);
}
}
for(int i = 1;i <= cnt1;++i){
addEdge(src,i,1,0);
}
for(int i = 1;i <= cnt2;++i){ //!!!!!!!! 变点的时候一定要注意啊!
addEdge(cnt1 + i,sink,1,0);
}
int res = min_cost_flow(src,sink,cnt1);
printf("%d\n",res);
}
int main()
{
//freopen("Input.txt","r",stdin);
while(scanf("%d%d",&N,&M),(N||M)){
for(int i = 0;i < N;++i){
scanf("%s",str[i]);
}
solve();
}
return 0;
}