题目:http://poj.org/problem?id=3565
神奇结论:当总边权最小时,任意两条边不相交!
转化为求二分图带权最小匹配。
可以用费用流做。但这里学一下km算法。
https://blog.csdn.net/c20180630/article/details/70175814
km算法适用于求二分图带权最大匹配,所以这里把边权取反。
核心思想在于给两部点带上顶标,通过顶标限制连边,调整顶标实现最优匹配。
一定要注意匈牙利的时候写上!ib[i]的限制!
找调整最小值的时候,也许不止是右部未匹配点,而是右部不在相等子图中的点都可以。
不知怎的,calc里的sqrt如果不写1.0*就会编译错误。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int N=105; const double INF=99999.99999,eps=1e-7; int n,x[N],y[N],pre[N]; double dis[N][N],da[N],db[N]; bool ia[N],ib[N]; double calc(int xa,int xb,int ya,int yb) {return sqrt(1.0*(xa-xb)*(xa-xb)+1.0*(ya-yb)*(ya-yb));} bool check(int cur) { ia[cur]=1; for(int i=1;i<=n;i++) if(!ib[i]&&fabs(da[cur]+db[i]-dis[cur][i])<eps)//!ib[i]&&fabs { ib[i]=1; if(!pre[i]||check(pre[i])) { pre[i]=cur;return true; } } return false; } int main() { scanf("%d",&n);int xx,yy; for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]); for(int i=1;i<=n;i++) { scanf("%d%d",&xx,&yy); da[i]=-INF; for(int j=1;j<=n;j++) dis[i][j]=-calc(xx,x[j],yy,y[j]),da[i]=max(da[i],dis[i][j]); } for(int i=1;i<=n;i++) { memset(ia,0,sizeof ia); memset(ib,0,sizeof ib); while(!check(i)) { double d=INF; for(int i=1;i<=n;i++) if(ia[i]) for(int j=1;j<=n;j++) if(!ib[j])//也许不是!pre[j] d=min(d,da[i]+db[j]-dis[i][j]);//符号 for(int i=1;i<=n;i++) { if(ia[i])da[i]-=d;if(ib[i])db[i]+=d; } memset(ia,0,sizeof ia); memset(ib,0,sizeof ib); } } for(int i=1;i<=n;i++) printf("%d ",pre[i]); return 0; }