題目概述
一張地圖,R行C列,圖上有若干人以及和人數相等的房子,人需要移動到房子,每個房子只能住一個人,人可以向上下左右四個方向移動,求所有人最少移動步數和,使得所有人都到房子處
人不會被其他人或房子阻擋,人可以移動到房子卻不住進去
時限
1000ms/3000ms
輸入
第一行兩個整數R,C,其後一個R行C列的矩陣,’.’代表空地,’m’代表一個人,’H’代表一個房子,輸入到R=C=0結束
限制
2<=R,C<=100;1<=房子數<=100
輸出
每行一個數,爲所求最少移動步數和
樣例輸入
2 2
.m
H.
5 5
HH..m
…..
…..
…..
mm..H
7 8
…H….
…H….
…H….
mmmHmmmm
…H….
…H….
…H….
0 0
樣例輸出
2
10
28
討論
圖論,網絡流,費用流,最小增廣路算法,構圖時,算出人到每個房子的曼哈頓距離(橫座標差的絕對值加縱座標差的絕對值),作爲費用,除此外其他邊的費用爲0,這樣就是源點,人,房子,匯點的圖,所有邊殘量都是1,剩下的就是固定算法了
實現方面,沒有什麼難度,需要稍微注意下數據範圍,人和房子各100,需要開到200大小
題解狀態
524K,47MS,C++,1721B
題解代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 104
#define memset0(a) memset(a,0,sizeof(a))
int RR, CC, N, S, T;//行數 列數 合計節點數 源點 匯點 由於R名稱被佔用了 所以換了個名字
char mat[MAXN][MAXN];//存放原始地圖
int mr[MAXN], mc[MAXN], hr[MAXN], hc[MAXN], m, h;//記錄人和房子所在的行列 以及人和房子的數量 類似於棧
int R[MAXN * 2][MAXN * 2], W[MAXN * 2][MAXN * 2], dis[MAXN * 2], from[MAXN * 2];//費用流的四數組 殘量矩陣 費用矩陣 最短路距離 父節點
queue<int>q;
bool inq[MAXN * 2];
int fun()
{
for (int p = 0; p < RR; p++)
scanf("%s", mat[p]);//input//讀圖
for (int p = 0; p < RR; p++)
for (int i = 0; i < CC; i++) {
if (mat[p][i] == 'm')//找出人
mr[m] = p, mc[m++] = i;
else if (mat[p][i] == 'H')//找出房子
hr[h] = p, hc[h++] = i;
}
N = (T = m + h + 1) + 1;//初始化總節點數和 匯點編號 源點是0
for (int p = 1; p <= m; p++) {
R[S][p] = 1;//源點到人
R[m + p][T] = 1;//房子到匯點 他們的費用都是0
}
for (int p = 0; p < m; p++)
for (int i = 0; i < h; i++) {
R[1 + p][m + 1 + i] = 1;//人到房子
W[m + 1 + i][1 + p] = -(W[1 + p][m + 1 + i] = abs(mr[p] - hr[i]) + abs(mc[p] - hc[i]));//費用爲曼哈頓距離 同時構造反向邊
}
int least = 0;//最小費用
while (1) {
int flow = 1;//初始流量 下面是改造過的bellman_ford算法 作爲最小增廣路的一部分
for (int p = 0; p < N; p++)
dis[p] = INF;
dis[S] = 0;
q.push(S);
while (!q.empty()) {
int a = q.front();
q.pop();
inq[a] = 0;
for (int p = 0; p < N; p++)
if (R[a][p] && dis[p] > dis[a] + W[a][p]) {
dis[p] = dis[a] + W[a][p];
from[p] = a;
flow = min(flow, R[a][p]);
if (!inq[p]) {
q.push(p);
inq[p] = 1;
}
}
}
if (dis[T] == INF)
return least;//從這裏返回
least += dis[T];
for (int p = T; p; p = from[p]) {
int i = from[p];
R[i][p] -= flow;
R[p][i] += flow;
}
}
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
while (~scanf("%d%d", &RR, &CC) && (RR || CC)) {//input
printf("%d\n", fun());//output
memset0(R);
memset0(W);
m = h = 0;//和棧一樣 只要清零棧頂的指示物就可以了
}
}
EOF