hdoj 1533 Going Home 【最小费用最大流】【KM入门题】

Going Home

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

Total Submission(s): 3443    Accepted Submission(s): 1763

Problem 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

第一道费用流。 O(∩_∩)O~~

题意:一个N*M地图上有同样数量的字符H和字符m。m代表一个 人,H代表一个房子。人到房子的花销是它们在图中的曼哈顿距离,问你让全部人回到房子所须要的最小费用(一个房子仅仅能容纳一个人)。

思路:设置超级源点source 连接全部字符H,容量为1。费用为0。每一个字符H向全部字符m连边,容量为1,费用为它们的曼哈顿距离。最后每一个字符m向超级汇点sink连边。容量为1,费用为0。

最后source到sink跑一遍最小费用最大流就ok了。

(我建边时把字符H看做人了,所以建的反向边,对这道题没有影响)

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define MAXN 200+10
#define MAXM 80000+100
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN], dist[MAXN];
bool vis[MAXN];
int N, M;
int source, sink;//超级源点 超级汇点
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
    Edge E1 = {u, v, w, 0, c, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, -c, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
int dis(int x1, int y1, int x2, int y2)
{
    return abs(x1 - x2) + abs(y1 - y2);
}
struct Node
{
    int x, y;
};
Node m[110], H[110];//存储字符坐标
int m_cnt;//m字符计数器
int H_cnt;//H字符计数器
void getMap()
{
    m_cnt = H_cnt = 0;
    char str[110][110];
    for(int i = 0; i < N; i++)
    {
        scanf("%s", str[i]);
        for(int j = 0; j < M; j++)
        {
            if(str[i][j] == ‘m‘)
            {
                ++m_cnt;
                m[m_cnt].x = i;
                m[m_cnt].y = j;
            }
            if(str[i][j] == ‘H‘)
            {
                ++H_cnt;
                H[H_cnt].x = i;
                H[H_cnt].y = j;
            }
        }
    }
    int k = m_cnt;//人数 或者 房子数
    source = 0;
    sink = 2*k+1;
    for(int i = 1; i <= k; i++)
    {
        addEdge(source, i, 1, 0);
        addEdge(i + k, sink, 1, 0);
        for(int j = 1; j <= k; j++)
        {
            int d = dis(H[i].x, H[i].y, m[j].x, m[j].y);
            addEdge(i, j + k, 1, d);
        }
    }
}
bool SPFA(int s, int t)//寻找花销最少的路径
{
    queue<int> Q;
    memset(dist, INF, sizeof(dist));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//能够松弛 且 没有满流
            {
                dist[E.to] = dist[u] + E.cost;
                pre[E.to] = i;//记录前驱边 的编号
                if(!vis[E.to])
                {
                    vis[E.to] = true;
                    Q.push(E.to);
                }
            }
        }
    }
    return pre[t] != -1;//可达返回true
}
void MCMF(int s, int t, int &cost, int &flow)
{
    flow = 0;//总流量
    cost = 0;//总费用
    while(SPFA(s, t))//每次寻找花销最小的路径
    {
        int Min = INF;
        //通过反向弧 在源点到汇点的最少花费路径 找最小增广流
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            Edge E = edge[i];
            Min = min(Min, E.cap - E.flow);
        }
        //增广
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;//增广流的花销
        }
        flow += Min;//流量累加
    }
}
int main()
{
    while(scanf("%d%d", &N, &M), N||M)
    {
        init();
        getMap();
        int cost, flow;
        MCMF(source, sink, cost, flow);
        printf("%d\n", cost);
    }
    return 0;
}

KM算法: 重刷

求最大流。用负权,最后结果取反。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 10000000
using namespace std;
int lx[110], ly[110];
int Map[110][110];
bool visx[110], visy[110];
int slack[110];
int match[110];
int nx, ny;
int N, M;
char str[110][110];
int dis(int x1, int y1, int x2, int y2)
{
    return abs(x1 - x2) + abs(y1 - y2);
}
struct Node
{
    int x, y;
};
Node m[110], H[110];//存储字符坐标
int m_cnt;//m字符计数器
int H_cnt;//H字符计数器
void getMap()
{
    m_cnt = H_cnt = 0;
    char str[110][110];
    for(int i = 0; i < N; i++)
    {
        scanf("%s", str[i]);
        for(int j = 0; j < M; j++)
        {
            if(str[i][j] == ‘m‘)
            {
                ++m_cnt;
                m[m_cnt].x = i;
                m[m_cnt].y = j;
            }
            if(str[i][j] == ‘H‘)
            {
                ++H_cnt;
                H[H_cnt].x = i;
                H[H_cnt].y = j;
            }
        }
    }
    int k = m_cnt;//人数 或者 房子数
    nx = ny = k;
    for(int i = 1; i <= k; i++)
    {
        for(int j = 1; j <= k; j++)
        {
            int d = dis(H[i].x, H[i].y, m[j].x, m[j].y);
            Map[i][j] = -d;
        }
    }
}
int DFS(int x)
{
    visx[x] = true;
    for(int y = 1; y <= ny; y++)
    {
        if(visy[y]) continue;
        int t = lx[x] + ly[y] - Map[x][y];
        if(t == 0)
        {
            visy[y] = true;
            if(match[y] == -1 || DFS(match[y]))
            {
                match[y] = x;
                return 1;
            }
        }
        else if(slack[y] > t)
            slack[y] = t;
    }
    return 0;
}
void KM()
{
    memset(match, -1, sizeof(match));
    memset(ly, 0, sizeof(ly));
    for(int x = 1; x <= nx; x++)
    {
        lx[x] = -INF;
        for(int y = 1; y <= ny; y++)
            lx[x] = max(lx[x], Map[x][y]);
    }
    for(int x = 1; x <= nx; x++)
    {
        for(int i = 1; i <= ny; i++)
            slack[i] = INF;
        while(1)
        {
            memset(visx, false, sizeof(visx));
            memset(visy, false, sizeof(visy));
            if(DFS(x)) break;
            int d = INF;
            for(int i = 1; i <= ny; i++)
            {
                if(!visy[i] && slack[i] < d)
                    d = slack[i];
            }
            for(int i = 1; i <= nx; i++)
            {
                if(visx[i])
                   lx[i] -= d;
            }
            for(int i = 1; i <= ny; i++)
            {
                if(visy[i])
                    ly[i] += d;
                else
                    slack[i] -= d;
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= ny; i++)
        ans += Map[match[i]][i];
    printf("%d\n", -ans);
}
int main()
{
    while(scanf("%d%d", &N, &M), N||M)
    {
        getMap();
        KM();
    }
    return 0;
}
时间: 2024-10-09 22:09:47

hdoj 1533 Going Home 【最小费用最大流】【KM入门题】的相关文章

POJ 2516 Minimum Cost(最小费用最大流,坑题)

题目链接:http://poj.org/problem?id=2516 题意:有N个店,M个供货商,K种商品.已知供货商的仓库里每种商品的数量以及每种商品运送到每个店的费用,每个店铺对各种商品的需求数量,求最少话费. Input  第一行:N,M,K. 然后1 - N行,每行 K列 ,第I行第J个数代表 第I个店铺 需要第J种物品多少件. 然后 N+1 - M行  ,每行 K列 , 第I行第J个数代表 第I个供货商 有第J种物品多少件. 然后是K个矩阵  ,每个N行M列,第ji个矩阵的第i行第j

hdu 1533 Going Home 最小费用最大流

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1533 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

hdu 1533 Going Home 最小费用最大流 入门题

Going Home Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3125    Accepted Submission(s): 1590 Problem Description On a grid map there are n little men and n houses. In each unit time, every

poj2135最小费用最大流经典模板题

Farm Tour Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13509   Accepted: 5125 Description When FJ's friends visit him on the farm, he likes to show them around. His farm comprises N (1 <= N <= 1000) fields numbered 1..N, the first of

POJ 2195 &amp; HDU 1533 Going Home(最小费用最大流)

题目链接: POJ:http://poj.org/problem?id=2195 HDU:http://acm.hdu.edu.cn/showproblem.php?pid=1533 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,

hdoj 3488 Tour 【最小费用最大流】【KM算法】

Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 2299    Accepted Submission(s): 1151 Problem Description In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 3000

hdoj 2282 Chocolate 【最小费用最大流】

Chocolate Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 565    Accepted Submission(s): 281 Problem Description Lethe loves eating chocolates very much. In Lethe's birthday, her good friend ec

POJ--2516--Minimum Cost【最小费用最大流】

链接:http://poj.org/problem?id=2516 题意:有k种货物,n个客户对每种货物有一定需求量,有m个仓库,每个仓库里有一定数量的k种货物,然后k个n*m的矩阵,告诉从各个仓库到各个客户位置运送单位第k种货物所需的运费,问满足所有客户需求的最小费用,如满足不了所有客户,则输出-1. 思路:题目有点绕,不过多看看也就理解了.这道题算是最小费用最大流的入门题吧,建图很容易能想到,主要是存在k种货物,每条货物都要建一条路,同时处理起来不好写,而且路径也较多,不过可以对每种货物分开

POJ 3422 Kaka&#39;s Matrix Travels 【最小费用最大流】

题意: 卡卡有一个矩阵,从左上角走到右下角,卡卡每次只能向右或者向下.矩阵里边都是不超过1000的正整数,卡卡走过的元素会变成0,问卡卡可以走k次,问卡卡最多能积累多少和. 思路: 最小费用最大流的题目. 建图自己没想出来,看了大神的建边,把每个点分解成两个点,一个代表进入一个代表出去,然后每个进入和每个出去连边,容量是1价值是这个点的矩阵的数值.然后因为可以不进去,所以起点要和别的矩阵元素的起点建边,终点也要和别的矩阵矩阵元素的起点建边,最后跑下最小费用最大流. 这题最右下角的矩阵元素需要特殊

POJ2135 最小费用最大流模板题

练练最小费用最大流 此外此题也是一经典图论题 题意:找出两条从s到t的不同的路径,距离最短. 要注意:这里是无向边,要变成两条有向边 #include <cstdio> #include <cstring> #define MAXN 1005 #define MAXM 10005 #define INF 0x3f3f3f3f struct Edge { int y,c,w,ne;//c容量 w费用 }e[MAXM*4]; int n,m,x,y,w; int s,t,Maxflow