訓練指南 UVALive - 4043(二分圖匹配 + KM算法)
阿新 • • 發佈:2019-02-03
!= 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算法)