2195 Going Home (構圖 最大匹配KM演算法)
阿新 • • 發佈:2019-01-30
題意:
‘.’ ,‘m’, ‘H’-> 通路, 人, 房子
讓所有人回到房子裡,使得總路徑最小
分析:
由題意可知,人與房的距離為權值,人和房子分別為x、y兩個集合,所以可以轉換成二部圖【權值最大】匹配問題,只要把路徑取【負值】,最後結果再取負即可
構圖之後直接使用KM演算法求解
KM演算法主要流程:
1)初始化,房子集合的頂標ly全為0,人集合的頂標lx全為最大值(人到其他房子距離),確保lx[i] + ly[j] >= Edge[i][j](第i人到第j房的距離(負值))
2)尋找完備匹配(一個集合匹配完全),使用匈牙利演算法
3)尋找失敗後,更新匹配,更新頂標
4)重複2~3,得出結果
程式碼:
#include <stdio.h> #include <iostream> #include <string.h> #include <string> #include <math.h> #include <algorithm> #include <queue> #include <stack> #include <vector> #include <map> using namespace std; #define MAX 101 #define MIN -1e9 #define INF 0x7f7f7f7f int t, n, m; int x[MAX], y[MAX]; // 每次查詢完美匹配時候,區分使用過 -- 1, 未使用過 -- 0 int Edge[MAX][MAX]; // Edge【i】【j】 第i個人到第j個房子的距離(負值) int lx[MAX], ly[MAX]; // 頂標 int linky[MAX]; // 匹配記錄 int path(int u) // 增廣路判斷 , 匈牙利演算法 { x[u] = 1; for(int v = 1; v<=m; v++) // 每個x遍歷所有y { if(y[v] == 1) continue; if(lx[u] + ly[v] == Edge[u][v]) // 可行頂標 { y[v] = 1; if(linky[v] == -1 || path(linky[v])) // y為匹配 || 更改匹配y的x成功 { linky[v] = u; return 1; } } } return 0; } void KM() { memset(linky, -1, sizeof(linky)); // 初始化 memset(ly, 0, sizeof(ly)); // y的頂標 for(int i = 1; i<=n; i++) // x的頂標 { lx[i] = -INF; for(int j = 1; j<=m; j++) { lx[i] = max(lx[i], Edge[i][j]); } } for(int i = 1; i<=n; i++) // 所有x都有增廣路 -> 完美匹配 { while(1) { memset(x, 0, sizeof(x)); memset(y, 0, sizeof(y)); if(path(i)) break; // 增廣路成功 int d = INF; // 失敗後,更換差值最小的邊 for(int j = 1; j<=n; j++) // 對於已經使用的x, 在所有為使用的y中查詢新邊,且更換的差值最小 { if(x[j] == 1) { for(int k = 1; k<=m; k++) { if(y[k] == 0 && d > lx[j]+ly[k]-Edge[j][k]) { d = lx[j] + ly[k] - Edge[j][k]; } } } } for(int j = 1; j<=n; j++) // x的頂標-d,確保lx[i] + ly[j] >= Edge[i][j] { if(x[j] == 1) lx[j] -= d; } for(int j = 1; j<=m; j++) <span style="font-family: Arial, Helvetica, sans-serif;"> // y的頂標+d,確保lx[i] + ly[j] >= Edge[i][j]</span> { if(y[j] == 1) ly[j] += d; } } } int ans = 0; for(int i = 1; i<=m; i++) { if(linky[i]!=-1) ans += Edge[linky[i]][i]; } printf("%d\n", -ans); } struct MAN { int x, y; }man[MAX*MAX]; struct HUOSE { int x, y; }house[MAX*MAX]; int main() { int i, j; //freopen("a.txt", "r", stdin); int nt, mt; while(scanf("%d%d", &nt, &mt) && nt + mt) { char mapt[MAX][MAX]; for(i = 0; i<nt; i++) { scanf("%s", mapt[i]); } n = m = 0; for(i = 0; i<nt; i++) { for(j = 0; j<mt; j++) { if(mapt[i][j] == 'm') { n++; man[n].x = i; man[n].y = j; } else if(mapt[i][j] == 'H') { m++; house[m].x = i; house[m].y = j; } } } for(i = 1; i<=n; i++) { for(j = 1; j<=m; j++) { Edge[i][j] = -(abs(man[i].x - house[j].x) +abs(man[i].y - house[j].y)); } } KM(); } return 0; }