POJ 2195-Going Home(KM演算法/最小費用最大流演算法)
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
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 Home(KM演算法/最小費用最大流演算法)
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