KM算法求二分图的最佳匹配
1.用于求带权二分图匹配边权值和最大的完备匹配(实际上就是每个左边的点都要匹配上)
2.顶表:一个用于衡量边是否在相等子图内的数值
3.相等子图:点还是原图的点,边是原图边集的子集,包含所有 端点顶标和 = 边权 的边。
4.时间复杂度:一般是0(n^3)
题目:
POJ3565
Ants
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 8089 Accepted: 2543 Special Judge
Description
Young naturalist Bill studies ants in school. His ants feed on plant-louses that live on apple trees. Each ant colony needs its own apple tree to feed itself.
Bill has a map with coordinates of n ant colonies and n apple trees. He knows that ants travel from their colony to their feeding places and back using chemically tagged routes. The routes cannot intersect each other or ants will get confused and get to the wrong colony or tree, thus spurring a war between colonies.
Bill would like to connect each ant colony to a single apple tree so that all n routes are non-intersecting straight lines. In this problem such connection is always possible. Your task is to write a program that finds such connection.
On this picture ant colonies are denoted by empty circles and apple trees are denoted by filled circles. One possible connection is denoted by lines.
Input
The first line of the input file contains a single integer number n (1 ≤ n ≤ 100) — the number of ant colonies and apple trees. It is followed by n lines describing n ant colonies, followed by n lines describing n apple trees. Each ant colony and apple tree is described by a pair of integer coordinates x and y (−10 000 ≤ x, y ≤ 10 000) on a Cartesian plane. All ant colonies and apple trees occupy distinct points on a plane. No three points are on the same line.
Output
Write to the output file n lines with one integer number on each line. The number written on i-th line denotes the number (from 1 to n) of the apple tree that is connected to the i-th ant colony.
Sample Input
5
-42 58
44 86
7 28
99 34
-13 -59
-47 -44
86 74
68 -75
-68 60
99 -60
Sample Output
4
2
1
5
3
题目大意:
就是给出黑色(蚂蚁群)的点和白色(苹果树)的点的坐标,然后在黑点 与 白点之间连边,然后连接的边不能够交叉。要求按顺序输出(1-n) 的黑点匹配的白色的点的下标。
解题思路:
要求连接的边不能够交叉,即让我们的边的和的长度最小,我们可以让点之间的距离看作这条边的权值,然后我们把每一条边的权值取相反数,用KM算法求最佳匹配,得到的匹配方案就是题中要求的权值最小匹配。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 105;
const double inf = 1e12;
const double eps = 1e-10;
struct Node {
double x, y;
}black[maxn], whilt[maxn];
int n;
double delta, w[maxn][maxn], la[maxn], lb[maxn];
bool va[maxn], vb[maxn];
int match[maxn], ans[maxn];
inline double get(Node a, Node b) {
return -sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
inline bool find(int x) {
va[x] = true;
for(int y = 1; y <= n; y ++) {
double tmp = la[x] + lb[y] - w[x][y];
if(fabs(tmp) < eps) {
if(!vb[y]) {
vb[y] = true;
if(!match[y] || find(match[y])) {
match[y] = x;
return true;
}
}
}
}
return false;
}
inline void KM(void) {
//初始化顶标
for(int i = 1; i <= n; i ++) {
la[i] = -inf;
for(int j = 1; j <= n; j ++) {
lb[j] = 0;
la[i] = max(la[i], w[i][j]);
}
}
for(int i = 1; i <= n; i ++)
while(true) {
memset(va, false, sizeof va);
memset(vb, false, sizeof vb);
if(find(i)) break;
delta = inf;
for(int x = 1; x <= n; x ++)
if(va[x])
for(int y = 1; y <= n; y ++)
if(!vb[y])
delta = min(delta, la[x] + lb[y] - w[x][y]);
for(int j = 1; j <= n; j ++) {
if(va[j]) la[j] -= delta;
if(vb[j]) lb[j] += delta;
}
}
for(int i = 1; i <= n; i ++)
ans[match[i]] = i;
}
int main(void) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%lf%lf", &black[i].x, &black[i].y);
for(int i = 1; i <= n; i ++)
scanf("%lf%lf", &whilt[i].x, &whilt[i].y);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
w[i][j] = get(black[i], whilt[j]);
KM();
for(int i = 1; i <= n; i ++)
printf("%d\n", ans[i]);
return 0;
}
注意点:
1.由于需要求距离,所以数据类型为 double
2.由于是double 类型,所以判断相等的时候我们需要用到eps, 因为是double,一般为eps = 1e-10.
3.double类型的最大值可以直接初始化为 1e12