POJ_3565 Ants KM匹配

http://poj.org/problem?id=3565

题意:

有N只ants和N棵tree,要求一种ants和tree的匹配方案,使得ant和tree之间的匹配线段互不相交。

思路:

KM求距离权值和最小的完备匹配,即我们可以证明距离权值和最小的完备匹配一定是不会出现

两条线段相交的情况。下面给出一个证明:我们假设最小完备匹配中有两条线段AC和BD相交于

点E,此时我们可以不连接AC和BD而去连接AD和BC,由于AE+DE>AD(三角形的性质)和BE+

CE>BC,所有我们可以得出新连接的边的权值和一定比原来的边的权值和小,这样就可以得到

一种权值和更小的匹配,这原来的匹配是最小带权和的匹配矛盾, 因此最小带权和的匹配中不会

出现有两条线段相交的情况。 

代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#define CC(m,what) memset(m ,what , sizeof(m))
#define FF(i , CH) for(int i=0;i<CH;i++)
const int MAXN = 110 ;
const int inf = (1<<30) ;
const double eps = 1e-6 ;

int N ;
int x1[MAXN] , yy1[MAXN] , x2[MAXN] , yy2[MAXN] ;
double w[MAXN][MAXN] ;
void calc(){
    FF(i , N)
        FF( j , N )
            w[i][j] = -sqrt( (x2[i] - x1[j])*(x2[i] - x1[j])*1.0 + (yy2[i] - yy1[j])*(yy2[i] - yy1[j])*1.0 );
}
int match[MAXN] ;
double lx[MAXN]  ,ly[MAXN] , slack[MAXN];
bool sx[MAXN]  ,sy[MAXN] ;
bool dfs(int u){
    sx[u] = 1 ;
    FF(v , N){
        if( sy[v]) continue ;
        double t = lx[u] + ly[v] - w[u][v] ;
        if( fabs(t)<eps ){
            sy[v] = 1 ;
            if( match[v]==-1 || dfs(match[v])){
                match[v] = u ;
                return true ;
            }
        }
        else if( slack[v] > t)
            slack[v] = t ;
    }
    return false ;
}
void KM(){
    CC(match , -1) ;
    FF(i , N){
        lx[i] = -inf ;
        FF( j , N ){
            if(lx[i] < w[i][j] )
                lx[i] = w[i][j] ;
        }
    }
    FF(i , N){
        FF( j , N )
            slack[j] = inf ;
        while(1){
            CC(sx, 0) ; CC(sy,0) ;
            if( dfs(i) )    break ;
            double d = inf ;
            FF(j , N){
                if( !sy[j] && d>slack[j])
                    d = slack[j] ;
            }
            FF(j , N){
                if(sx[j])
                    lx[j] -= d ;
            }
            FF(j ,N){
                if( sy[j] )
                    ly[j] += d;
                else
                    slack[j] -= d ;
            }
        }
    }
    FF(i , N){
        printf("%d\n",match[i] + 1) ;
    }
}
int main(){
    while(scanf("%d",&N) == 1){
        FF(i ,N)    scanf("%d%d",&x1[i] ,&yy1[i]);
        FF(i ,N)    scanf("%d%d",&x2[i] ,&yy2[i]);
        calc() ;
        KM() ;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值