1. 程式人生 > >訓練指南 UVALive - 4043(二分圖匹配 + KM算法)

訓練指南 UVALive - 4043(二分圖匹配 + KM算法)

!= clas str 圖論 平面 bits res 點線 live


layout: post
title: 訓練指南 UVALive - 4043(二分圖匹配 + KM算法)
author: "luowentaoaa"
catalog: true
mathjax: true
tags:
- 二分圖匹配
- 圖論
- 訓練指南


Ants

UVALive - 4043

題意

給你n個白點和n個黑點的平面坐標,要求用n條不相交的線連起來,每條線段連一個白點和黑點,每個點連一條線,也就是匹配。讓你輸出第i個白點所對應的黑點。

思路

二分圖完美匹配問題。但是題目中有個線段不相交,怎麽辦?其實這個最佳完美匹配就是答案了。最佳完美匹配是權值和最大,那麽我們就把兩兩點線段的權值搞成他們距離的負數即可。這樣就不可能有相交的了。為什麽?因為假設有相交,a1-b2,a2-b1,而dist(a1,b1)+dist(a2,b2) 肯定比前面交叉的小,畫個四邊形就很清楚了,那麽負數就是大了,也就是說交叉的在我們設計的負權那裏是小的,所以就是最佳,也就是不可能有交叉的。

這樣分析清楚了之後,就只要直接套用KM就OK了!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e2+50;
const double inf=999999999999999.;
const double eps=1e-5;
struct node{
    double x,y;
}white[150],black[150];
double g[150][150];
int nx,ny;
bool visx[maxn],visy[maxn];
double slack[maxn];
int linker[maxn];
double lx[maxn],ly[maxn];
bool dfs(int x){
    visx[x]=true;
    for(int y=0;y<ny;y++){
        if(visy[y])continue;
        double tmp=lx[x]+ly[y]-g[x][y];
        if(fabs(tmp)<eps){
            visy[y]=true;
            if(linker[y]==-1||dfs(linker[y])){
                linker[y]=x;return true;
            }
        }
        else if(slack[y]>tmp)slack[y]=tmp;
    }
    return false;
}
int KM(){
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(int i=0;i<nx;i++){
        lx[i]=-inf;
        for(int j=0;j<ny;j++){
            if(g[i][j]>lx[i])lx[i]=g[i][j];
        }
    }
    for(int x=0;x<nx;x++){
        for(int i=0;i<ny;i++)slack[i]=inf;
        while(true){
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(dfs(x))break;
            double d=inf;
            for(int i=0;i<ny;i++)
                if(!visy[i]&&d>slack[i])d=slack[i];
            for(int i=0;i<nx;i++)
                if(visx[i])lx[i]-=d;
            for(int i=0;i<ny;i++)
                if(visy[i])ly[i]+=d;
                else slack[i]-=d;
        }
    }
    int res=0;
    for(int i=0;i<ny;i++)
        if(linker[i]!=-1)res+=g[linker[i]][i];
    return res;
}

double dis(node a,node b){
    return double(sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
int n;
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int first=0;
    while(cin>>n){
        nx=ny=n;
        if(first)cout<<endl;
        first=1;
        for(int i=0;i<n;i++){
            cin>>white[i].x>>white[i].y;
        }
        for(int i=0;i<n;i++){
            cin>>black[i].x>>black[i].y;
        }
        for(int i=0;i<n;i++)for(int j=0;j<n;j++)g[i][j]=-dis(black[i],white[j]);
        KM();
        for(int i=0;i<n;i++)cout<<linker[i]+1<<endl;
    }
    return 0;
}

訓練指南 UVALive - 4043(二分圖匹配 + KM算法)