最小化配对点的距离

Minimizing the distance of pairing points

我的问题如下:

Given a number of 2n points, I can calculate the distance between all points 
and get a symmetrical matrix.

Can you create n pairs of points, so that the sum of the distance of all pairs is 
minimal?

EDIT: Every point has to be in one of the pairs. Which means that
every point is only allowed to be in one pair.

我曾经天真地尝试过使用匈牙利算法,希望它能给我一个赋值,让赋值对称。但这显然行不通,因为我没有二分图。

经过搜索,我找到了Stable roommates problem,这似乎和我的问题很相似,但不同的是,它只是试图找到一个匹配,而不是试图最小化某种距离.

有没有人知道类似的问题或解决方案?我错过了什么?这个问题其实看起来并没有那么难,但是我就是想不出一个最优解。

由于 Edmonds(Blossom 算法),有一个原始对偶算法,如果可能的话,您真的不想自己实现。 Vladimir Kolmogorov 有一个 implementation 可能适合您的目的。

试试网络流量。最大流量是您要创建的对数。并计算它的最小成本。

现在这不是保证,只是预感。

你可以找到最短的一对,匹配它们,然后将其从集合中移除。

并递归直到你没有剩下的对。

分明是sub-optimal。但我有一种预感,sub-optimal 这与绝对最佳解决方案的比率是有界的。希望是使用一些 sub-modularity 参数并将其绑定到全局最优的 (1 - 1 / e) 部分,但我无法做到。也许有人可以尝试一下。

Competitive Programming 3 中有一个 C++ memoization 实现如下(注意 N 的最大值为 8):

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>

using namespace std;

int N, target;
double dist[20][20], memo[1<<16];

double matching(int bitmask)
{
    if (memo[bitmask] > -0.5)    // Already computed? Then return the result if yes
        return memo[bitmask];
    if (bitmask == target)       // If all students are already matched then cost is zero
        return memo[bitmask] = 0;

    double ans = 2000000000.0;        // Infinity could also work
    int p1, p2;

    for (p1 = 0; p1 < 2*N; ++p1)      // Find first non-matched point
        if (!(bitmask & (1 << p1)))
            break;
    for (p2 = p1 + 1; p2 < 2*N; ++p2) // and pair it with another non-matched point
        if (!(bitmask & (1 << p2)))
            ans = min(ans, dist[p1][p2]+matching(bitmask| (1 << p1) | (1 << p2)));

    return memo[bitmask] = ans;

}

然后是main方法(驱动代码)

int main()
{
    int i,j, caseNo = 1, x[20], y[20];

    while(scanf("%d", &N), N){
         for (i = 0; i < 2 * N; ++i)
             scanf("%d %d", &x[i], &y[i]);
         for (i = 0; i < 2*N - 1; ++i)
             for (j = i + 1; j < 2*N; ++j)
                  dist[i][j] = dist[j][i] = hypot(x[i]-x[j], y[i]-y[j]);

         // use DP to solve min weighted perfect matching on small general graph
         for (i = 0; i < (1 << 16); ++i) memo[i] = -1;
         target = (1 << (2 * N)) - 1;
         printf("Case %d: %.2lf", caseNo++, matching(0));

    }
    return 0;

}