1,如果二分圖不是完全二分圖,我們通過添加無用路徑(最大比對中,路徑權值為0)和頂點使之成為完全二分圖;
2,使用KM算法求解,KM算法核心需要了解feasible vertex labeling和equality subgraph概念,在equality subgraph中尋找最大比對(采用匈牙利算法),如果最大比對正好為完全比對,根據KM理論,這個完全比對就是帶權值的最大比對;如果在目前的equality subgraph擷取的最大比對不是完全比對,我們通過KM算法中提供的修改label方法,增加新的y點,得到新的equality subgraph,再繼續在新的subgraph尋找最大比對,如此循環。
代碼如下:
[cpp] view plain copy
- #include <cstdio>
- #include <memory.h>
- #include <algorithm> // 使用其中的 min 函數
- using namespace std;
- const int MAX = 1024;
- int n; // X 的大小
- int weight[MAX][MAX]; // X 到 Y 的映射(權重)
- int lx[MAX], ly[MAX]; // 标号
- bool sx[MAX], sy[MAX]; // 是否被搜尋過
- int match[MAX]; // Y(i) 與 X(match [i]) 比對
- // 初始化權重
- void init(int size);
- // 從 X(u) 尋找增廣道路,找到則傳回 true
- bool path(int u);
- // 參數 maxsum 為 true ,傳回最大權比對,否則最小權比對
- int bestmatch(bool maxsum = true);
- void init(int size)
- {
- // 根據實際情況,添加代碼以初始化
- n = size;
- for (int i = 0; i < n; i++)
- for (int j = 0; j < n; j++)
- scanf("%d", &weight[i][j]);
- }
- bool path(int u)
- {
- sx[u] = true;
- for (int v = 0; v < n; v++)
- if (!sy[v] && lx[u] + ly[v] == weight[u][v])
- {
- sy[v] = true;
- if (match[v] == -1 || path(match[v]))
- {
- match[v] = u;
- return true;
- }
- }
- return false;
- }
- int bestmatch(bool maxsum)
- {
- int i, j;
- if (!maxsum)
- {
- for (i = 0; i < n; i++)
- for (j = 0; j < n; j++)
- weight[i][j] = -weight[i][j];
- }
- // 初始化标号
- for (i = 0; i < n; i++)
- {
- lx[i] = -0x1FFFFFFF;
- ly[i] = 0;
- for (j = 0; j < n; j++)
- if (lx[i] < weight[i][j])
- lx[i] = weight[i][j];
- }
- memset(match, -1, sizeof(match));
- for (int u = 0; u < n; u++)
- while (1)
- {
- memset(sx, 0, sizeof(sx));
- memset(sy, 0, sizeof(sy));
- if (path(u)) //一直尋找增廣路徑,直到子圖中沒有增廣路徑,我們通過修改label來增加新的點,增加的點必為y
- break;
- // 修改标号
- int dx = 0x7FFFFFFF;
- for (i = 0; i < n; i++)
- if (sx[i])
- for (j = 0; j < n; j++)
- if (!sy[j])
- dx = min(lx[i] + ly[j] - weight[i][j], dx); //找到松弛變量最小的點
- for (i = 0; i < n; i++)
- {
- if (sx[i])
- lx[i] -= dx;
- if (sy[i])
- ly[i] += dx;
- }
- }
- int sum = 0;
- for (i = 0; i < n; i++)
- sum += weight[match[i]][i];
- if (!maxsum)
- {
- sum = -sum;
- for (i = 0; i < n; i++)
- for (j = 0; j < n; j++)
- weight[i][j] = -weight[i][j]; // 如果需要保持 weight [ ] [ ] 原來的值,這裡需要将其還原
- }
- return sum;
- }
- int main()
- {
- int n;
- scanf("%d", &n);
- init(n);
- int cost = bestmatch(true);
- printf("%d ", cost);
- for (int i = 0; i < n; i++)
- {
- printf("Y %d -> X %d ", i, match[i]);
- }
- return 0;
- }
附帶poj2195解法:
[cpp] view plain copy
- #include <cstdio>
- #include <cstdlib>
- #include <string.h>
- #include <algorithm>
- #include <cmath>
- using namespace std;
- #define MAX 200
- typedef struct GRID
- {
- int x;
- int y;
- }grid, pgrid;
- grid M[MAX], H[MAX];
- int n; // X 的大小
- int weight[MAX][MAX]; // X 到 Y 的映射(權重)
- int lx[MAX], ly[MAX]; // 标号
- bool sx[MAX], sy[MAX]; // 是否被搜尋過
- int match[MAX]; // Y(i) 與 X(match [i]) 比對
- bool path(int u)
- {
- sx[u] = true;
- for (int v = 0; v < n; v++)
- if (!sy[v] && lx[u] + ly[v] == weight[u][v])
- {
- sy[v] = true;
- if (match[v] == -1 || path(match[v]))
- {
- match[v] = u;
- return true;
- }
- }
- return false;
- }
- int bestmatch(bool maxsum)
- {
- int i, j;
- if (!maxsum)
- {
- for (i = 0; i < n; i++)
- for (j = 0; j < n; j++)
- weight[i][j] = -weight[i][j];
- }
- // 初始化标号
- for (i = 0; i < n; i++)
- {
- lx[i] = -0x1FFFFFFF;
- ly[i] = 0;
- for (j = 0; j < n; j++)
- if (lx[i] < weight[i][j])
- lx[i] = weight[i][j];
- }
- memset(match, -1, sizeof(match));
- for (int u = 0; u < n; u++)
- while (1)
- {
- memset(sx, 0, sizeof(sx));
- memset(sy, 0, sizeof(sy));
- if (path(u)) //一直尋找增廣路徑,直到子圖中沒有增廣路徑,我們通過修改label來增加新的點,增加的點必為y
- break;
- // 修改标号
- int dx = 0x7FFFFFFF;
- for (i = 0; i < n; i++)
- if (sx[i])
- for (j = 0; j < n; j++)
- if (!sy[j])
- dx = min(lx[i] + ly[j] - weight[i][j], dx); //找到松弛變量最小的點
- for (i = 0; i < n; i++)
- {
- if (sx[i])
- lx[i] -= dx;
- if (sy[i])
- ly[i] += dx;
- }
- }
- int sum = 0;
- for (i = 0; i < n; i++)
- sum += weight[match[i]][i];
- if (!maxsum)
- {
- sum = -sum;
- for (i = 0; i < n; i++)
- for (j = 0; j < n; j++)
- weight[i][j] = -weight[i][j]; // 如果需要保持 weight [ ] [ ] 原來的值,這裡需要将其還原
- }
- return sum;
- }
- int main()
- {
- int row, col;
- int i, j;
- int ch;
- int mCount, hCount;
- while(1)
- {
- scanf("%d %d", &row, &col);
- getchar();
- if(row == 0 && col == 0)
- break;
- mCount = 0;
- hCount = 0;
- for(i = 0; i < row ; i++)
- {
- for(j = 0; j < col; j++)
- {
- ch = getchar();
- if(ch == 'm')
- {
- M[mCount].x = i;
- M[mCount].y = j;
- mCount++;
- }
- else
- if(ch == 'H')
- {
- H[hCount].x = i;
- H[hCount].y = j;
- hCount++;
- }
- }
- getchar();
- }
- n = mCount;
- for(i = 0 ; i < n; i++)
- for(j = 0; j < n; j++)
- {
- weight[i][j] = abs(M[i].x - H[j].x) + abs(M[i].y - H[j].y);
- }
- printf("%d\n", bestmatch(false));
- }
- return 0;
- }