最佳完美匹配性质1poj3565-边上的性质

本文详细介绍了Kuhn-Munkres算法的实现原理及应用,通过具体的代码示例展示了如何解决点对点匹配问题,并提供了两种不同的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//注意边要反号
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
using namespace std;
const double eps = 1e-8;
struct pointt{
	double x, y;
	pointt(double x, double y) :x(x), y(y)
	{}
	pointt()
	{}
};
pointt pointx[110], pointy[110];
double map[110][110], valuex[110], valuey[110],slack[110];
int match[110], visitx[110], visity[110];
int n;
double dist(pointt a, pointt b)
{
	return -sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));//这里一定要反号因为这个算法是求得是在有完美匹配时的最大值
}
bool dfs(int num)
{
	visitx[num] = 1;
	for (int i = 1; i <= n; i++)
	{
		if (visity[i])continue;//这如果visity[i]=1就不要再dfs了有阔能成环
		double t = valuex[num] + valuey[i] -map[num][i];
		if (t<=eps)
		{
			visity[i] = 1;
			if (match[i] == -1 || dfs(match[i]))//如果哦visity[i]=0那么他的match的visitx一定也是0
			{
				match[i] = num;
				return true;
			}
		}
		else
			slack[i] = min(slack[i], t);
	}
	return false;
}
void km()
{ 
	for (int k = 1; k <= n; k++)match[k] = -1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)slack[j] = 1000000000;
		while (1)
		{
			for (int k = 1; k <= n; k++)visitx[k] = 0, visity[k] = 0;
			if (dfs(i))break;
			double d = 8000000000;
			for (int k = 1; k <= n; k++)
				if (!visity[k])
					d = min(slack[k], d);
			for (int k = 1; k <= n; k++)
			{
				if (visitx[k])
					valuex[k] -= d;
				if (visity[k])
					valuey[k] += d;
				//else
					//slack[k] -= d;完全可以不要这个,因为反正到时都是取最小
			}	
		}
	}
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lf%lf", &pointx[i].x, &pointx[i].y);
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%lf%lf", &pointy[i].x, &pointy[i].y);
	}
	for (int i = 1; i <= n; i++)
	{
		valuex[i] = -8000000000;//注意边是负数所以这样初始化
		for (int j = 1; j <= n; j++)
		{
			map[i][j] = dist(pointx[i], pointy[j]);
			valuex[i] = max(valuex[i], map[i][j]);
		}
	}
	km();
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (match[j] == i)
				printf("%d\n", j);
		}
	}
	return 0;
}

此题还阔以不用反号,说白了km算法就是贪心加在一定条件下的匹配

所以我们稍稍改一下条件就好让valuex[i]+valuey[j]<=w[i][j]在这个条件下进行匹配,然后贪心地从最小边开始然后慢慢增加。。具体如下:

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

struct Node
{
	double x, y;
}Dot1[MAXN], Dot2[MAXN];

double Dist(Node a, Node b)
{
	return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

int N, NX, NY;
double Map[MAXN][MAXN];
int link[MAXN];
double lx[MAXN], ly[MAXN], slack[MAXN];
int visx[MAXN], visy[MAXN];

int FindPath(int u)
{
	visx[u] = 1;
	for (int i = 1; i <= NY; ++i)
	{
		if (visy[i])
			continue;
		double temp = Map[u][i] - lx[u] -ly[i];
		if (fabs(temp) <= eps)
		{
			visy[i] = 1;
			if (link[i] == -1 || (FindPath(link[i])))
			{
				link[i] = u;
				return 1;
			}
		}
		else
		{
			if (slack[i] > temp)
				slack[i] = temp;
		}
	}
	return 0;
}

void KM()
{
	for (int i = 0; i <= NX; i++)lx[i] = 8000000000;
	memset(ly, 0, sizeof(ly));
	memset(link, -1, sizeof(link));
	for (int i = 1; i <= NX; ++i)
	{
		for (int j = 1; j <= NY; ++j)
		{
			lx[i] = min(lx[i], Map[i][j]);
		}
	}

		for (int i = 1; i <= NX; ++i)
		{
			for (int j = 1; j <= NY; ++j)
				slack[j] = INF;
			while (1)
			{
				memset(visx, 0, sizeof(visx));
				memset(visy, 0, sizeof(visy));
				if (FindPath(i))
					break;
				double d = INF;
				for (int j = 1; j <= NY; ++j)
					if (!visy[j] && d > slack[j])
						d = slack[j];
				for (int j = 1; j <= NX; ++j)
					if (visx[j])
						lx[j] += d;
				for (int j = 1; j <= NY; ++j)
				{
					if (visy[j])
						ly[j] -= d;
					else
						slack[j] -= d;
				}
			}
		}
}

int main()
{
	int N;
	scanf("%d", &N);
	memset(Map, 0, sizeof(Map));
	for (int i = 1; i <= N; ++i)
		scanf("%lf%lf", &Dot1[i].x, &Dot1[i].y);
	for (int i = 1; i <= N; ++i)
		scanf("%lf%lf", &Dot2[i].x, &Dot2[i].y);

	for (int i = 1; i <= N; ++i)
		for (int j = 1; j <= N; ++j)
		{
		Map[i][j] = Dist(Dot1[i], Dot2[j]);
		//cout << Map[i][j] << endl;
		}

	NX = NY = N;
	KM();
	for (int i = 1; i <= N; ++i)
	{
		for (int j = 1; j <= N; ++j)
		{
			if (link[j] == i)
			{
				printf("%d\n", j);
				break;
			}
		}
	}


	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值