poj3565 Ants(二分图最小权匹配,KM算法)

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

题意:给你n的蚂蚁的坐标和n和苹果的坐标,要你匹配它们,一个要求是它们各自匹配所构成的线段不相交,

题解: 有三角形俩边之和大于第三边可以得到他们连线的所有线段和最小的时候线段一定不会相交。
所以这个题目就转化为二分图最小权匹配,把俩点距离堪称权值,套下KM算法就ok了

#include <iostream>
#include <cstring>
#include <cstdio>
#include<cmath> 
using namespace std;
const int MAXN = 115; 
double eps = 1e-6;
const double INF = 0x3f3f3f3f;

double dis[MAXN][MAXN];   
double ex_bai[MAXN];      
double ex_hei[MAXN];       
bool vis_hei[MAXN];    
bool vis_bai[MAXN];     
int match[MAXN];        
double slack[MAXN];        
int N;

struct node{
	int x, y;
}ant[MAXN],apple[MAXN];
 
 double min(double a,double b)
{
	return a<b?a:b;
} 
double max(double a,double b)
{
 	return a>b?a:b;
}
bool dfs(int hei)
{
    vis_hei[hei] = true;

    for (int bai = 1; bai <= N; ++bai) {

        if (vis_bai[bai]) continue; 

        double  gap = ex_hei[hei] + ex_bai[bai] - dis[hei][bai];
        if (fabs(gap)<=1e-6) {  // 如果符合要求
            vis_bai[bai] = true;
            if (match[bai] == -1 || dfs( match[bai] )) {   
                match[bai] = hei;
                return true;
            }
        } else {
            slack[bai] = min(slack[bai], gap);  
        }
    }

    return false;
}

void KM()
{
    memset(match, -1, sizeof (match));    
    memset(ex_bai, 0, sizeof (ex_bai));   


    for (int i = 1; i <= N; ++i) {
        ex_hei[i] = -INF;		//最小值这个时候不能是0了,而是-INF
        for (int j = 1; j <= N; ++j) {
            ex_hei[i] = max(ex_hei[i], dis[i][j]);
        }
    }

    for (int i = 1; i <=N; ++i) {

        fill(slack+1, slack + N+1, INF);    // 因为要取最小值 初始化为无穷大

        while (1) {
        	
            memset(vis_hei, false, sizeof vis_hei);
            memset(vis_bai, false, sizeof vis_bai);

            if (dfs(i)) break;  // 找到归宿 退出

            // 如果不能找到 就降低期望值
            // 最小可降低的期望值
            double d = INF;
            for (int j = 1; j <=N; ++j)
                if (!vis_bai[j]) d = min(d, slack[j]);

            for (int j = 1; j <=N; ++j) {
                
				if (vis_hei[j]) ex_hei[j] -= d;

                if (vis_bai[j]) ex_bai[j] += d;
            
                else slack[j] -= d;
            }
        }
    }
}

double dist(int i,int j)
{
	double x=sqrt((ant[i].x-apple[j].x)*(ant[i].x-apple[j].x)+(ant[i].y-apple[j].y)*(ant[i].y-apple[j].y));
	return x;
}
int main()
{
 		scanf("%d",&N);
		for(int i=1;i<=N;i++)
		{
			scanf("%d %d",&ant[i].x,&ant[i].y);	
		}
		for(int i=1;i<=N;i++)
		{
			scanf("%d %d",&apple[i].x,&apple[i].y);
		}
		for(int i=1;i<=N;i++)
		{
			for(int j=1;j<=N;j++)
			{
				dis[j][i]=-dist(i,j);//取反
			//	printf("%.3lf ",dis[j][i]); 
			}
		}
		KM();
		for(int i=1;i<=N;i++)
		{
			printf("%d\n",match[i]);
		}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值