Uvalive 4043 - Ants(二分图完美匹配)

题目链接 https://vjudge.net/problem/UVALive-4043

【题意】
       给出n个白点和n个黑点的坐标,要求用n条不相交的线段把它们连起来,其中每条线段恰好连接一个白点和一个黑点,每个点恰好连接到一条线段

【输入格式】
       多组输入,第一行是整数n(1<=n<=100),然后有n行整数代表每个白点的坐标,之后又有n行整数代表每个黑点的坐标,坐标的绝对值不超过10000,保证所有点不重合,且任意三点不共线,白点和黑点的编号按照输入顺序为1~n

【输出格式】
       每组数据输出n行,第i行是第i个白点所连接到的黑点的编号,多组数据之间用空行隔开

【思路】
       根据黑白两种不同颜色的结点构造二分图,每个白点和黑点相连权值为两点的欧几里德距离,这样建立模型后,求出二分图的最佳完美匹配即可,当然这里指的是权值和最小的完美匹配。因为假设权值和最小的完美匹配中有a1-a2,b1-b2相交,根据两点间直线距离最短或三角形任意两边之和大于第三边可知dist(a1,b1)+dist(a2,b2)>dist(a1,b2)+dist(a2,b1),因此如果按a1-b2,a2-b1这样匹配后的权值之和更小,与最佳完美匹配相矛盾,也就是说,最佳完美匹配不可能有线段相交的情况。
       而求权值和最小的最佳完美匹配也不难,只需把各结点之间的权值改为欧几里德距离的相反数后再求最大权匹配即可

#include<bits/stdc++.h>
using namespace std;

typedef double type;

const type inf = 2e9;
const double eps = 1e-8;
const int maxn = 330;

int n, m;
int matchx[maxn], matchy[maxn];
int visx[maxn], visy[maxn];
type lx[maxn], ly[maxn];
type w[maxn][maxn];
type slack[maxn];

struct Point {
    type x, y;
    Point(type xx = 0, type yy = 0) :x(xx), y(yy) {}
}white[maxn], black[maxn];

type dis(Point& a, Point& b) {
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

bool dfs(int x) {
    visx[x] = 1;
    for (int y = 0; y < m; ++y) {
        if (visy[y]) continue;
        type tmp = lx[x] + ly[y] - w[x][y];
        if (fabs(tmp) <= eps) {
            visy[y] = 1;
            if (matchy[y] == -1 || dfs(matchy[y])) {
                matchx[x] = y;
                matchy[y] = x;
                return true;
            }
        }
        else {
            slack[y] = min(slack[y], tmp);
        }
    }
    return false;
}

void KM() {
    memset(matchy, -1, sizeof(matchy));
    memset(ly, 0, sizeof(ly));
    for (int i = 0; i < n; ++i) {
        lx[i] = -inf;
        for (int j = 0; j < m; ++j) {
            lx[i] = max(lx[i], w[i][j]);
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) slack[j] = inf;
        while (1) {
            memset(visx, 0, sizeof(visx));
            memset(visy, 0, sizeof(visy));
            if (dfs(i)) break;
            type d = inf;
            for (int j = 0; j < m; ++j) {
                if (!visy[j]) d = min(d, slack[j]);
            }
            for (int j = 0; j < n; ++j) { if (visx[j]) lx[j] -= d; }
            for (int j = 0; j < m; ++j) {
                if (visy[j]) ly[j] += d;
                else slack[j] -= d;
            }
        }
    }
}

int main() {
    int kase = 0;
    while (scanf("%d", &n) == 1) {
        if (kase) puts("");
        kase = 1;
        m = n;
        for (int i = 0; i < n; ++i) {
            scanf("%lf%lf", &white[i].x, &white[i].y);
        }
        for (int i = 0; i < m; ++i) {
            scanf("%lf%lf", &black[i].x, &black[i].y);
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                w[i][j] = -dis(white[i], black[j]);//权值设置为距离的相反数
            }
        }
        KM();
        for (int i = 0; i < n; ++i) {
            printf("%d\n", matchx[i] + 1);
        }
    }
    return 0;
}
posted @ 2018-03-15 21:23  不想吃WA的咸鱼  阅读(120)  评论(0编辑  收藏  举报