#KM演算法#UVA1411 Ants
阿新 • • 發佈:2022-03-02
KM演算法
題目
在一個平面直角座標系中,有 \(n\) 個黑點,\(n\) 個白點。
給出一種二分圖匹配的方案,使得沒有兩條由黑白點連線的線段相交
分析
如果線段都不相交,根據三角形的兩邊之和大於第三邊,那麼線段的長度之和一定是最小的。
那麼這道題就轉化成二分圖最大權完美匹配,用KM演算法寫就可以了。
程式碼
#include <cstdio> #include <cctype> #include <cmath> #include <queue> using namespace std; const int N=111; bool vx[N],vy[N]; typedef double db; queue<int>q; db slack[N],lx[N],ly[N],G[N][N]; int px[N],py[N],link[N],n,x[N],y[N]; int iut(){ int ans=0,f=1; char c=getchar(); while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar(); while (isdigit(c)) ans=ans*10+c-48,c=getchar(); return ans*f; } void print(int ans){ if (ans>9) print(ans/10); putchar(ans%10+48); } db min(db a,db b){return a<b?a:b;} db max(db a,db b){return a>b?a:b;} void adjust(int y){ for (int _y;y;y=_y){ _y=px[link[y]]; px[link[y]]=y; py[y]=link[y]; } } void bfs(int st){ for (int i=1;i<=n;++i) slack[i]=1e12,vx[i]=vy[i]=0; while (!q.empty()) q.pop(); q.push(st); while (1){ while (!q.empty()){ int x=q.front(); vx[x]=1,q.pop(); for (int y=1;y<=n;++y) if (!vy[y]&&slack[y]>lx[x]+ly[y]-G[x][y]){ slack[y]=lx[x]+ly[y]-G[x][y],link[y]=x; if (!slack[y]){ vy[y]=1; if (!py[y]) {adjust(y); return;} else q.push(py[y]); } } } db mn=1e12; for (int i=1;i<=n;++i) if (!vy[i]) mn=min(mn,slack[i]); for (int i=1;i<=n;++i){ if (vx[i]) lx[i]-=mn; if (vy[i]) ly[i]+=mn; else slack[i]-=mn; } for (int i=1;i<=n;++i) if (!vy[i]&&!slack[i]){ vy[i]=1; if (!py[i]) {adjust(i); return;} else q.push(py[i]); } } } void KM(){ for (int i=1;i<=n;++i){ link[i]=ly[i]=px[i]=py[i]=0,lx[i]=-1e12; for (int j=1;j<=n;++j) lx[i]=max(lx[i],G[i][j]); } for (int i=1;i<=n;++i) bfs(i); } int o(int x){return x*x;} int main(){ while (scanf("%d",&n)==1){ for (int i=1;i<=n;++i) x[i]=iut(),y[i]=iut(); for (int j=1;j<=n;++j){ int X=iut(),Y=iut(); for (int i=1;i<=n;++i) G[i][j]=-sqrt(o(X-x[i])+o(Y-y[i])); } KM(); for (int i=1;i<=n;++i) print(px[i]),putchar(10); putchar(10); } return 0; }