1. 程式人生 > >POJ 2195-Going Home(KM演算法/最小費用最大流演算法)

POJ 2195-Going Home(KM演算法/最小費用最大流演算法)

Going Home
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 21730 Accepted: 10990

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


題目意思:

N行M列,每人可分得一個房子,每人每走一步花費為1,求每人各回各家的最小花費。

解題思路:

這題有兩種思路可破:KM演算法和最小費用最大流演算法。

(一)

套Kuhn-Munkras演算法模板,建二分圖求解。

下面詳細講講建圖的過程

①掃描讀入的圖,分別標記出人M房屋H的位置座標;

②每個人與每間房屋一一對應,比如測試用例

5 5
HH..m
.....
.....
.....
mm..H
M的座標是A(1,5) B(5,1) C(5,2);H的座標是X(1,1) Y(1,2) Z(5,5);

那麼分別求出AX AY AZ BX BY BZ CX CY CZ路徑上對應花費的權值就是二分圖中對應邊的邊權。
因為是求最小權,所以建圖時邊的權值取相反數,最後所得的ans取相反數。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<malloc.h>
using namespace std;
typedef long long ll;
#define maxn 300
#define INF 0x3f3f3f3f
int nx,ny;//兩邊的點數
int g[maxn][maxn];//二分圖描述
int linker[maxn],lx[maxn],ly[maxn];//y中各點匹配狀態,x,y中的點標號
int slack[maxn];
bool visx[maxn],visy[maxn];
int ax[maxn],ay[maxn],bx[maxn],by[maxn];
char Map[maxn][maxn];
bool DFS(int x)
{
    visx[x] = true;
    for(int y = 1; y <= ny; y++)
    {
        if(visy[y])continue;
        int tmp = lx[x] + ly[y] - g[x][y];
        if(tmp == 0)
        {
            visy[y] = true;
            if(linker[y] == -1 || DFS(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if(slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}
int KM()
{
    memset(linker,-1,sizeof(linker));
    memset(lx,0,sizeof(lx));
    memset(ly,0,sizeof(ly));
    for(int i = 0; i < nx; i++)
    {
        lx[i] = -INF;
        for(int j = 1; j <= ny; j++)
            if(g[i][j] > lx[i])
                lx[i] = g[i][j];
    }
    for(int x = 1; x <= nx; x++)
    {
        for(int i = 1; i <= ny; i++)
            slack[i] = INF;
        while(true)
        {
            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] && d > slack[i])
                    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 res = 0;
    for(int i = 1; i <= nx; i++)
        if(linker[i] != -1)
            res += g[linker[i]][i];
    return -res;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)&&n!=0&&m!=0)
    {
        /*for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                scanf("%d",&g[i][j]);
        nx = ny = n;*/
        memset(g,0,sizeof g);
        nx=ny=0;//二分圖頂點的個數
        for(int i=1; i<=n; i++)
            scanf("%s",Map[i]+1);//讀入原圖
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                if(Map[i][j]=='m')
                {
                    nx++;
                    ax[nx]=i;
                    ay[nx]=j;
                }
                else if(Map[i][j]=='H')
                {
                    ny++;
                    bx[ny]=i;
                    by[ny]=j;
                }
            }
        for(int i=1; i<=nx; i++)
            for(int j=1; j<=ny; j++)
                g[i][j]=-(abs(ax[i]-bx[j])+abs(ay[i]-by[j]));//建圖
        printf("%d\n",KM());
    }
    return 0;
}
/**
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
**/

(二)

套最小費用最大流演算法模板,加超級源點和超級匯點網路流求解。

注意模板使用的時候,minCostMaxflow()的返回值。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<malloc.h>
using namespace std;
typedef long long ll;
#define maxn 300

const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;//節點總個數,節點編號從0~N-1
void init(int n)
{
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)//加邊
{
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s,int t)//找增廣路
{
    queue<int>q;
    for(int i = 0; i <= t+2; i++)
    {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[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)
        {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost )
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;
    else return true;
}
//int minCostMaxflow(int s,int t,int &cost)
int minCostMaxflow(int s,int t)//最小費用最大流
{
    int flow = 0;
    int ans=0;
    //cost = 0;
    while(spfa(s,t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].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;
        }
        ans+=dis[t];
        flow += Min;
    }
    return ans;
}

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)&&n!=0&&m!=0)
    {
        int nx,ny,ax[maxn],ay[maxn],bx[maxn],by[maxn];
        char Map[maxn][maxn];
        nx=ny=0;//二分圖頂點的個數
        for(int i=1; i<=n; i++)
            scanf("%s",Map[i]+1);//讀入原圖
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                if(Map[i][j]=='m')
                {
                    nx++;
                    ax[nx]=i;
                    ay[nx]=j;
                }
                else if(Map[i][j]=='H')
                {
                    ny++;
                    bx[ny]=i;
                    by[ny]=j;
                }
            }
        int s=0,t=nx+ny+1;//源點和匯點
        init(t+1);//初始化
        for(int i = 1; i <= nx; i++)
            addedge(s,i,1,0);//源點向各個點加邊
        for(int i = nx+1; i <= nx+ny; i++)
            addedge(i,t,1,0);//匯點向各個點加邊
        for(int i=1; i<=nx; i++)
            for(int j=1; j<=ny; j++)
            {
                int va=(abs(ax[i]-bx[j])+abs(ay[i]-by[j]));
                //cout<<i<<"->"<<nx+j<<"="<<va<<endl;
                addedge(i,nx+j,1,va);//建圖
            }
        printf("%d\n",minCostMaxflow(s,t));
    }
    return 0;
}
/**
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0
**/


相關推薦

POJ 2195-Going HomeKM演算法/費用演算法

Going Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 21730 Accepted: 10990 Description On a grid map there are n

POJ 2195 Going Home權匹配、KM演算法

題目連結: POJ 2195 Going Home 題意: 給出一個r*c的矩陣,字母H代表房屋,字母m代表客人,房屋的數量和客人的數量相同。每間房只能住一個人。求這些客人全部住進客房的最少移動步數? #include <cstdio>

POJ 2195 Going Home費用題解

題意:給你一張圖,有k個人和k個房子,每個房子只能住一個人,每個人到某一房子的花費為曼哈頓距離,問你讓k個人怎麼走,使他們都住房子且花費最小。 思路:我們把所有人和超級源點相連,流量為1花費為0,所有房子和超級匯點相連,流量為1花費為0,然後把所有人和所有房子加邊,流量為1

POJ 2195 Going Home 費用

題目連結 第一次寫最小費用最大流 最小費用最大流相當於最短路和最大流的結合,建立了殘量圖後,費用作為距離 然後bfs遍歷源點到匯點的最小費用,用陣列記錄遍歷路徑,而後通過最大流的做法 對殘量圖進行更新,尋找路徑中的最小流量得到的就是最小費用最大流。 題

POJ 2195 Going Home費用

void 個人 nod const 移動 方向 push class main http://poj.org/problem?id=2195 題意: 在一個網格地圖上,有n個小人和n棟房子。在每個時間單位內,每個小人可以往水平方向或垂直方向上移動一步,走到相鄰的方格中。

POJ 2195 Going Home網路-費用

Going Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 17777 Accepted: 9059 Description On a grid map there are n

POJ-2195 Going Home---KM算法求權值匹配存負邊

for 二分圖 ostream lse ons nod esp 範圍 預處理 題目鏈接: https://vjudge.net/problem/POJ-2195 題目大意: 給定一個N*M的地圖,地圖上有若幹個man和house,且man與house的數量一致。man每移動

2195 Going Home 構圖 匹配KM演算法

題意: ‘.’ ,‘m’, ‘H’-> 通路, 人, 房子 讓所有人回到房子裡,使得總路徑最小 分析: 由題意可知,人與房的距離為權值,人和房子分別為x、y兩個集合,所以可以轉換成二部圖【權值最大】匹配問題,只要把路徑取【負值】,最後結果再取負即可 構圖之後直接使用

HDU 2255 奔小康賺大錢 POJ 2195 Going Home 大權完美匹配 KM演算法

兩道KM演算法模板題 可以作為求最大完美匹配模板 一個是求最大權,一個求最小權 ,最小權可以將所有的邊權取相反數,求得最大權之後再取反。 HDU 2255程式碼: /*--------------------- #headfile--------------------*

Going Home POJ - 2195 費用

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

Going Home POJ - 2195 費用

open more ons column prev unit cst font -s On a grid map there are n little men and n houses. In each unit time, every little man can mov

POJ 2195 Going Home(二分圖大權值匹配) KM

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

[poj] 2195 Going Home || 費用

etc 移動 數量 print getc truct pop namespace -c 原題 給定一個N*M的地圖,地圖上有若幹個人和房子,且人與房子的數量一致。人每移動一格需花費1(即單位費用=單位距離),一間房子只能入住一個人。現在要求所有的人都入住,求最小費用。 把每

POJ 2195 Going Home 【二分圖權值匹配】

傳送門:http://poj.org/problem?id=2195 Going Home Time Limit: 1000MS   Memory Limit: 65536K Tota

poj-2195 費用or二分圖km演算法

Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26221  

POJ 2195 Going Home(KM)

人是可以踩踏房子經過的,那距離就很好求了。建圖求距離,把距離設為負的,就轉化為KM了,最後別忘了負號。 #include <iostream> #include <cstring> #include <cstdio> #include

2195 Going Home 網路

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 ver

2195 Going Home 費用

Going HomeTime Limit: 1000MSMemory Limit: 65536KTotal Submissions: 24299Accepted: 12196DescriptionOn a grid map there are n little men and

Going Home poj 2195 費用

題目大意 給定一個N*M的地圖,地圖上有若干個man和house,且man與house的數量一致。man每移動一格需花費$1(即單位費用=單位距離),一間house只能入住一個man。現在要求所有的man都入住house,求最小費用。 分析 費用流的模板套

POJ 2195 Going Home(費用)

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, ei