poj 2195

題目概述

一張地圖,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

發佈了208 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章