1. 程式人生 > >POJ-2195 Going Home---KM算法求最小權值匹配(存負邊)

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

for 二分圖 ostream lse ons nod esp 範圍 預處理

題目鏈接:

https://vjudge.net/problem/POJ-2195

題目大意:

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

思路:

KM算法傳送門: 理解篇 運用篇

每個man和house建立帶權二分圖,曼哈頓距離就是邊的值,這裏要求最小費用,也就是二分圖最小權值匹配,但是KM算法求的是二分圖最大權值匹配,所以此處用邊的負數求最優匹配,求出來的答案的負數就是最小權匹配。

註意:題目說house最多100,但是沒有說明man的範圍,所以man應該最多100*100。

應該用house為二分圖的X部,因為算法復雜度和X部點數有關,所以用點數少的house為X部

因為存的是負數,在預處理X部的頂標值初始化應該是-INF,不能是0

剩下的就是模板啦

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxx = 100 + 5;//house的上限
  7 const int maxy = 10000 + 5;//man的上限
  8
const int INF = 0x3f3f3f3f; 9 int cntx, cnty;//X部的點的數目,Y部點的數目 10 bool visx[maxx], visy[maxy];//是否加入增廣路 11 int wx[maxx], wy[maxy];//頂標值 12 int cx[maxx], cy[maxy];//匹配的點 13 int minz;//頂標值和邊權最小的差值 14 int Map[maxx][maxy];//保存邊 15 16 bool dfs(int u) 17 { 18 visx[u] = 1; 19 for(int v = 1; v <= cnty; v++)
20 { 21 if(!visy[v])//還未加入增廣路 22 { 23 int t = wx[u] + wy[v] - Map[u][v]; 24 //計算邊權和頂標之差,為0表示是相等子圖 25 if(t == 0) 26 { 27 visy[v] = 1; 28 if(cy[v] == -1 || dfs(cy[v]))//還未匹配或者反向找到增廣路 29 { 30 cy[v] = u; 31 cx[u] = v; 32 //cout<<u<<"v"<<v<<endl; 33 return 1; 34 } 35 } 36 else if(t > 0)minz = min(minz, t); 37 } 38 } 39 return 0; 40 } 41 42 int KM() 43 { 44 memset(cx, -1, sizeof(cx)); 45 memset(cy, -1, sizeof(cy)); 46 memset(wx, -INF, sizeof(wx));//註意,這裏存的是負邊,所以初始化必須是負無窮 47 memset(wy, 0, sizeof(wy)); 48 for(int i = 1; i <= cntx; i++)//預處理出X部的頂標值 49 { 50 for(int j = 1; j <= cnty; j++) 51 { 52 wx[i] = max(wx[i], Map[i][j]); 53 //cout<<wx[i]<<endl; 54 } 55 } 56 for(int i = 1; i <= cntx; i++) 57 { 58 while(1) 59 { 60 minz = INF; 61 memset(visx, 0, sizeof(visx)); 62 memset(visy, 0, sizeof(visy)); 63 if(dfs(i))break; 64 65 for(int j = 1; j <= cntx; j++) 66 if(visx[j])wx[j] -= minz; 67 for(int j = 1; j <= cnty; j++) 68 if(visy[j])wy[j] += minz; 69 } 70 } 71 int ans = 0; 72 for(int i = 1; i <= cntx; i++) 73 if(cx[i] != -1)ans += Map[i][cx[i]]; 74 return ans; 75 } 76 struct node 77 { 78 int x, y; 79 node(){} 80 node(int x, int y):x(x), y(y){} 81 }; 82 node house[maxx], man[maxy]; 83 char M[105][105]; 84 int main() 85 { 86 int n, m; 87 while(cin >> n >> m && (n && m)) 88 { 89 cntx = cnty = 0; 90 for(int i = 0; i < n; i++)//讀圖 91 { 92 cin >> M[i]; 93 for(int j = 0; j < m; j++) 94 { 95 if(M[i][j] == H)house[++cntx] = node(i, j); 96 else if(M[i][j] == m)man[++cnty] = node(i, j); 97 } 98 } 99 100 for(int i = 1; i <= cntx; i++)//建圖 101 { 102 for(int j = 1; j <= cnty; j++) 103 { 104 Map[i][j] = -abs(house[i].x - man[j].x) - abs(house[i].y - man[j].y); 105 } 106 } 107 cout<<(-KM())<<endl; 108 } 109 return 0; 110 }

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