POJ2195 Going Home 【最小费用流】+【二分图最佳匹配】

Going Home

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 18169   Accepted: 9268

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

Pacific Northwest 2004

题意:给定一张N*M的图,其中‘.’为空地,小人可以走,‘m’为小人,‘H’为房子,小人可以路过,小人一次只能沿着上下左右走一个格子。现在要求每个小人都进入一个不同的房子,求小人走的步数最小数。

题解:这题可以看成最小费用流来解,其中小人到房子的距离为费用,容量为1,设置源点到每个小人的容量为1,费用为0,每个房子到汇点费用为0,容量为1,剩下的就是求最小费用流了。

版本一:最小费用流:125ms

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>

const int maxn = 205;
const int inf = 0x3f3f3f3f;
char str[maxn];
int head[maxn], n, m; // n rows, m columns
struct Node {
    int x, y;
} A[maxn], B[maxn];
int id1, id2, id, source, sink;
struct node {
    int f, c, u, v, next;
} E[maxn * maxn];
bool vis[maxn];
int pre[maxn], dist[maxn];

void addEdge(int u, int v, int c, int f) {
    E[id].u = u; E[id].v = v;
    E[id].c = c; E[id].f = f;
    E[id].next = head[u]; head[u] = id++;

    E[id].u = v; E[id].v = u;
    E[id].c = 0; E[id].f = -f;
    E[id].next = head[v]; head[v] = id++;
}

void getMap() {
    int i, j, dis; Node e;
    id = id1 = id2 = 0;
    for(i = 0; i < n; ++i) {
        scanf("%s", str);
        for(j = 0; str[j] != '\0'; ++j) {
            if(str[j] == '.') continue;
            e.x = i; e.y = j;
            if(str[j] == 'm') A[id1++] = e;
            else B[id2++] = e;
        }
    }

    memset(head, -1, sizeof(head));
    source = id1 + id2; sink = source + 1;
    for(i = 0; i < id1; ++i) {
        for(j = 0; j < id2; ++j) {
            dis = abs(A[i].x - B[j].x) + abs(A[i].y - B[j].y);
            addEdge(i, id1 + j, 1, dis); // uvcf
        }
        addEdge(source, i, 1, 0);
    }
    for(j = 0; j < id2; ++j)
        addEdge(id1 + j, sink, 1, 0);
}

bool SPFA(int start, int end) {
    std::queue<int> Q; int i, u, v;
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    memset(dist, 0x3f, sizeof(pre));
    Q.push(start); vis[start] = 1; dist[start] = 0;
    while(!Q.empty()) {
        u = Q.front(); Q.pop();
        vis[u] = 0;
        for(i = head[u]; i != -1; i = E[i].next) {
            v = E[i].v;
            if(E[i].c && dist[v] > dist[u] + E[i].f) {
                dist[v] = dist[u] + E[i].f;
                pre[v] = i;
                if(!vis[v]) {
                    Q.push(v); vis[v] = 1;
                }
            }
        }
    }
    return dist[end] != inf;
}

int Min_Cost_Flow(int start, int end) {
    int ans_cost = 0, u, minCut;
    while(SPFA(start, end)) {
        minCut = inf;
        for(u = pre[end]; u != -1; u = pre[E[u].u]) {
            if(minCut > E[u].c) minCut = E[u].c;
        }
        for(u = pre[end]; u != -1; u = pre[E[u].u]) {
            E[u].c -= minCut; E[u^1].c += minCut;
        }
        ans_cost += minCut * dist[end];
    }
    return ans_cost;
}

void solve() {
    printf("%d\n", Min_Cost_Flow(source, sink));
}

int main() {
    // freopen("stdin.txt", "r", stdin);
    while(scanf("%d%d", &n, &m), n | m) {
        getMap();
        solve();
    }
    return 0;
}

版本二:KM:0ms

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

const int maxn = 105;
const int largeNum = 210;
const int inf = 0x3f3f3f3f;
int n, m; // n rows, m columns
char str[maxn];
struct Node {
    int x, y;
} A[maxn], B[maxn];
int id1, id2;
int G[maxn][maxn];
int Lx[maxn], Ly[maxn];
int match[maxn];
bool visx[maxn], visy[maxn];
int slack[maxn];

void getMap() {
    int i, j, dis; Node e;
    id1 = id2 = 0;
    for(i = 0; i < n; ++i) {
        scanf("%s", str);
        for(j = 0; str[j] != '\0'; ++j) {
            if(str[j] == '.') continue;
            e.x = i; e.y = j;
            if(str[j] == 'm') A[id1++] = e;
            else B[id2++] = e;
        }
    }
    memset(G, 0, sizeof(G));
    for(i = 0; i < id1; ++i) {
        for(j = 0; j < id2; ++j) {
            G[i][j] = largeNum - (abs(A[i].x - B[j].x) + abs(A[i].y - B[j].y));
        }
    }
}

bool DFS(int cur) {
    int t, y;
    visx[cur] = true;
    for(y = 0; y < id2; ++y) {
        if(visy[y]) continue;
        t = Lx[cur] + Ly[y] - G[cur][y];
        if(t == 0) {
            visy[y] = true;
            if(match[y] == -1 || DFS(match[y])) {
                match[y] = cur; return true;
            }
        } else if(slack[y] > t) slack[y] = t;
    }
    return false;
}

int KM() {
    int i, j, x, d, ret;
    memset(match, -1, sizeof(match));
    memset(Ly, 0, sizeof(Ly));
    for(i = 0; i < id1; ++i) {
        Lx[i] = -inf;
        for(j = 0; j < id2; ++j)
            if(G[i][j] > Lx[i]) Lx[i] = G[i][j];
    }
    for(x = 0; x < id1; ++x) {
        memset(slack, 0x3f, sizeof(slack));
        while(true) {
            memset(visx, 0, sizeof(visx));
            memset(visy, 0, sizeof(visy));
            if(DFS(x)) break;
            d = inf;
            for(i = 0; i < id2; ++i)
                if(!visy[i] && d > slack[i])
                    d = slack[i];
            for(i = 0; i < id1; ++i)
                if(visx[i]) Lx[i] -= d;
            for(i = 0; i < id2; ++i)
                if(visy[i]) Ly[i] += d;
                else slack[i] -= d;
        }
    }
    ret = 0;
    for(i = 0; i < id1; ++i)
        if(match[i] > -1) ret += G[match[i]][i];
    return ret;
}

void solve() {
    printf("%d\n", largeNum * id1 - KM());
}

int main() {
    // freopen("stdin.txt", "r", stdin);
    while(scanf("%d%d", &n, &m), n | m) {
        getMap();
        solve();
    }
    return 0;
}
时间: 2024-10-09 09:09:20

POJ2195 Going Home 【最小费用流】+【二分图最佳匹配】的相关文章

POJ2195 Going Home【二分图最佳匹配】

题目链接: http://poj.org/problem?id=2195 题目大意: 在一个N*M的矩阵中,有M个人和M个房子,每个人要安排一个房子,每个房子只能安排一个人. 而每个人移动一步需要一美元.那么问题来了:求为每个人安排房子移动所需要的金钱最小值是多 少. 思路: 做一个二分图,一边为人,另一边为房子,如果把人和房子之间的距离作为边权的话,问题就变成 了求带权二分图最小权和的最佳匹配.这里我们为了方便计算,吧人和房子之间的距离的负值作为 边权,那么就变成了求带权二分图最大权和的最佳匹

hdu2255 奔小康赚大钱 二分图最佳匹配--KM算法

传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子住的话,容易引起不安定因素),每家必须分配到一间房子且只能得到一间房子.另一方面,村长和另外的村领导希望得到最大的效益,这样村里的机构才会有钱.由于老百姓都比较富裕,他们都能对每一间房子在他们的经济范围内出一定的价格,比如有3间房子,一家老百姓可以对第一间出10万,对第2间出2万,对第3间出20万.(

Regionals 2015 &gt;&gt; Asia - Tehran &gt;&gt; 7530 - Cafebazaar【二分图最佳匹配】【KM】【最大费用流】

Regionals 2015 >> Asia - Tehran >> 7530 - Cafebazaar 题目链接:7530 题目大意:一个公司有n个开发者,有m个APP可开发.其中一些开发者必选,一些APP必选.已知每个开发者开发每个APP的收益,求最大收益.(每个开发者最多开发一个APP,每个APP最多一个人开发) 题目思路: 解法一:二分图最佳匹配(KM算法) 增加一些虚开发者和虚app,非必要app可以被虚开发者开发,收益为0,反过来非必要开发者可以开发虚app,收益为0.

HDU2255 奔小康赚大钱【二分图最佳匹配】

题目链接: http://acm.hdu.edu.cn/showproblem.php? pid=2255 题目大意: 村里要分房子. 有N家老百姓,刚好有N间房子.考虑到每家都要有房住,每家必须分配到一间房子且 仅仅能分配到一间房子.另外, 村长为了得到最大利益,让老百姓对房子进行估价. 比方有3件房子,一 家老百姓能够对第一间出10万,对第二间出2万,对第三间出4万.第二家老百姓能够对第一间出8万, 对第二家出3万,对第三间出5万.那么问题来了:怎么分配,才干使利益最大化. (村民房子不一定

二分图最佳匹配,求最大权匹配或最小权匹配

Beloved Sons http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1338 题意:国王有N个儿子,现在每个儿子结婚都能够获得一定的喜悦值,王子编号为1-N,有N个女孩的编号同样为1-N,每个王子心中都有心仪的女孩,现在问如果安排,能够使得题中给定的式子和最大. 分析:其实题目中那个开根号是个烟雾弹,只要关心喜悦值的平方即可.那么对王子和女孩之间构边,边权为喜悦值的平方,对于每一个王子虚拟出一个女孩边权为0,这样是为了所

二分图最佳匹配

1 /* 2 * this code is made by bjfu_song 3 * Problem: 1227 4 * Verdict: Accepted 5 * Submission Date: 2014-10-05 14:53:22 6 * Time: 132MS 7 * Memory: 2340KB 8 */ 9 #include<iostream> 10 #include<stdio.h> 11 #include<string.h> 12 #include&

HDU2255 奔小康赚大钱 【二分图最佳匹配&#183;KM算法】

奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3898    Accepted Submission(s): 1691 Problem Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考

二分图&#39;最佳匹配&#39; KM算法

讲得很清楚的博客  推荐! 点我?? 附上一模板题 // hdu 2255(求最大权和) 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int INF = 0x3f3f3f3f; 5 const int MAXN = 300+5; 6 7 int n; 8 int w[MAXN][MAXN];// 权值数组 9 int cx[MAXN], cy[MAXN];// "标杆&qu

hdu 2063 过山车(二分图最佳匹配)

经典的二分图最大匹配问题,因为匈牙利算法我还没有认真去看过,想先试试下网络流的做法,即对所有女生增加一个超级源,对所有男生增加一个超级汇,然后按照题意的匹配由女生向男生连一条边,跑一个最大流就是答案(以上所有边容量均为 1 ),我是直接上 Dinic 算法的模板的: 1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<queue> 5 #include<algorit

POJ3565 Ants【二分图最佳匹配】

题目链接: http://poj.org/problem?id=3565 题目大意: 在坐标系中有N只蚂蚁,N棵苹果树,给你蚂蚁和苹果树的坐标.让每只蚂蚁去一棵苹果树, 一棵苹果树对应一只蚂蚁.这样就有N条直线路线,问:怎样分配,才能使总路程和最小,且 N条线不相交. 思路: 用一个图来说明思路. 假设A.B为蚂蚁,C.D为苹果树.则存在两种匹配:第一种是AD.BC,第二种是AC.BD. 根据三角形不等式AD+BC < AC+BD,最后得到很重要的一个性质--满足总路程之和最小 的方案一定不相交