zoukankan      html  css  js  c++  java
  • POJ3565 Ants 空间点对不相交匹配最小权值匹配

    题意:给定平面上两类同样多的点,要求输出一种方案使得所有匹配的点的连线两两不相交。

    解法:考虑到下面的一般情况:

    可以很容易的证明两条交叉边的距离和一定大于两条不交叉的距离和,因此问题转化为只要原图中存在交叉边,那么就可以找到更小的匹配的方式使得总距离更小。使用KM算法求出最小权值匹配输出匹配方案即可。

    代码如下:

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cmath>
    using namespace std;
    
    int N;
    double w[105][105];
    int sx[105], sy[105];
    double lx[105], ly[105];
    double slack[105];
    int match[105];
    
    struct Node {
        int x, y;    
    }c[105], t[105];
    
    double dist(const Node &a, const Node &b) {
        return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
    }
    
    void build() {
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) {
                w[i][j] = -dist(c[i], t[j]);
            }
        }
    }
    
    int path(int u) {
        sx[u] = 1;
        for (int i = 1; i <= N; ++i) {
            if (sy[i]) continue;
            double t = lx[u] + ly[i] - w[u][i];
            if (fabs(t) < 1e-6) { // 如果这个值等于0 
                sy[i] = 1;
                if (!match[i] || path(match[i])) {
                    match[i] = u;
                    return true;    
                }
            } else {
                slack[i] = min(slack[i], t);
            }
        }
        return false;
    }
    
    void KM() {
        memset(match, 0, sizeof (match));
        fill(ly, ly+105, 0);
        fill(lx, lx+105, 0);
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) { // 为i选择一个最大的权值 
                lx[i] = max(lx[i], w[i][j]);
            }
        }
        for (int i = 1; i <= N; ++i) { // 每一次都要为i找到一条增广路
            fill(slack, slack+105, 1e30);
            while (1) {
                memset(sx, 0, sizeof (sx));
                memset(sy, 0, sizeof (sy));
                if (path(i)) break;
                double d = 1e30;
                for (int j = 1; j <= N; ++j) {
                    if (!sy[j])    d = min(d, slack[j]);
                }
                for (int j = 1; j <= N; ++j) {
                    if (sx[j]) lx[j] -= d;
                    if (sy[j]) ly[j] += d;
                    else slack[j] -= d;
                }
            }
        }
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) {
                if (match[j] == i) {
                    printf("%d\n", j);
                    break;
                }
            }
        }
    }
    
    int main() {
        while (scanf("%d", &N) != EOF) {
            for (int i = 1; i <= N; ++i)    {
                scanf("%d %d", &c[i].x, &c[i].y);
            }
            for (int i = 1; i <= N; ++i)    {
                scanf("%d %d", &t[i].x, &t[i].y);
            }
            build();
            KM();
        }
        return 0;    
    }
  • 相关阅读:
    Job 失败了怎么办?- 每天5分钟玩转 Docker 容器技术(133)
    用 k8s 运行一次性任务
    运行自己的 DaemonSet
    查看内核页表kernel_page_tables (aarch32)
    Linux内存管理学习3 —— head.S中的段页表的建立
    Linux内存管理学习2 —— head.S中的段页表的建立
    Linux内存管理学习1 —— head.S中的段页表的建立
    阅读Linux内核源码时建立tags索引
    在TQ2440上运行perf,生成Flame Graph
    Linux驱动开发——指针和错误值
  • 原文地址:https://www.cnblogs.com/Lyush/p/3024244.html
Copyright © 2011-2022 走看看