1. 程式人生 > >2195 Going Home (最小費用最大流)

2195 Going Home (最小費用最大流)

Going Home
Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 24299Accepted: 12196

Description

On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters a house. The task is complicated with the restriction that each house can accommodate only one little man. 

Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point. 

You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.

Input

There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.

Output

For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.

Sample Input

2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0

Sample Output

2
10
28

Source



題意:簡單易懂

解題思路:帶權二分匹配可做。但是用來學習最小費用最大流是極好的~!此處用來儲存模板。

我的理解就是,在求增廣路徑的時候,以費用為權值用最短路徑演算法去求增廣路徑,然後再增廣,這樣不僅能求出最大流,還能求出最小費用。這題感覺就是求 “最小流”。

人是多源點,房子是多匯點,建圖。

超級源點到每個人的容量為1,花費為0;房子到超級匯點也是容量為1,花費為0. 

人與房之間的容量無限大,花費為人與房之間的距離。 

這樣就是求從超級源點到超級匯點的最小費用最大流了。
#include <iostream>
#include <deque>
#include <stdio.h>
#include <map>
#include <string.h>
#include <algorithm>
#include <vector>
#include <math.h>
#include <stack>
#include <queue>
#include <set>
using namespace std;
typedef long long int ll;
const int MAXN = 100005;
const int INF = 0x3f3f3f3f;

int N, M;
char maze[200][200];
int cnt = 1;
int in[MAXN];
int on[MAXN];

//最小費用最大流部分!!
struct edge
{
    int u, v, cap, cost, next;
} e[4 * MAXN];
int edge_num;
int head[MAXN];

void insert_edge(int u, int v, int cap, int cost)
{
    e[edge_num].u = u;
    e[edge_num].v = v;
    e[edge_num].cap = cap;
    e[edge_num].cost = cost;
    e[edge_num].next = head[u];
    head[u] = edge_num++;

    //反向邊 k^1即可求出反向邊
    e[edge_num].u = v;
    e[edge_num].v = u;
    e[edge_num].cap = 0;      //注意這裡
    e[edge_num].cost = -cost; //注意這裡
    e[edge_num].next = head[v];
    head[v] = edge_num++;
}

int dis[MAXN]; //記錄最短距離
int pre[MAXN]; //記錄最短路徑(記錄邊)
bool vis[MAXN];

bool spfa(int s, int t)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));

    dis[s] = 0;
    vis[s] = 1;
    queue<int> que;
    que.push(s);
    while (!que.empty())
    {
        int tp = que.front();
        que.pop();
        vis[tp] = 0;
        for (int i = head[tp]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            int cost = e[i].cost;

            if (e[i].cap && dis[v] > dis[tp] + cost)
            {
                dis[v] = dis[tp] + cost;
                pre[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    if (dis[t] == INF)
        return false;
    return true;
}

pair<int, int> Min_Cost_Max_Flow(int s, int t)
{
    int maxflow = 0;
    int mincost = 0;
    int minc;
    while (spfa(s, t))
    {
        minc = INF;
        for (int i = pre[t]; ~i; i = pre[e[i].u])
            minc = min(minc, e[i].cap);
        for (int i = pre[t]; ~i; i = pre[e[i].u])
        {
            e[i].cap -= minc;
            e[i ^ 1].cap += minc;
        }
        mincost += dis[t];
        maxflow += minc;
    }
    return make_pair(mincost, maxflow);
}

int main()
{

    while (~scanf("%d%d", &N, &M))
    {
        if (N == 0 && M == 0)
            break;

        memset(head, -1, sizeof(head));
        edge_num = 0;
        cnt = 1;

        vector<pair<int, int> > HH;
        vector<pair<int, int> > mm;

        for (int i = 1; i <= N; i++)
        {
            scanf("%s", maze[i] + 1);
            for (int j = 1; j <= M; j++)
            {
                if (maze[i][j] == 'H')
                    HH.push_back(make_pair(i, j));
                if (maze[i][j] == 'm')
                    mm.push_back(make_pair(i, j));
            }
        }

        int S = cnt++;
        for (int i = 0; i < mm.size(); i++)
            in[i] = cnt++;
        for (int i = 0; i < HH.size(); i++)
            on[i] = cnt++;

        int T = cnt++;

        for (int i = 0; i < mm.size(); i++)
        {
            insert_edge(S, in[i], 1, 0);
        }

        for (int i = 0; i < HH.size(); i++)
        {
            insert_edge(on[i], T, 1, 0);
        }

        for (int i = 0; i < mm.size(); i++)
            for (int j = 0; j < HH.size(); j++)
                insert_edge(in[i], on[j], INF, abs(mm[i].first - HH[j].first) + abs(mm[i].second - HH[j].second));

        printf("%d\n", Min_Cost_Max_Flow(S, T).first);
    }

    return 0;
}