NOI 4.3 1538: Gopher II(匈牙利算法求最大匹配)

74 篇文章 2 订阅
19 篇文章 0 订阅

题目来源:http://noi.openjudge.cn/ch0403/1538/

1538: Gopher II

总时间限制2000ms   内存限制65536kB

描述

The gopher family, having averted the canine threat, must face anew predator. 

The are n gophers and m gopher holes, each at distinct (x, y) coordinates. Ahawk arrives and if a gopher does not reach a hole in s seconds it isvulnerable to being eaten. A hole can save at most one gopher. All the gophersrun at the same velocity v. The gopher family needs an escape strategy thatminimizes the number of vulnerable gophers.

输入

The input contains several cases. The first line of each casecontains four positive integers less than 100: n, m, s, and v. The next n linesgive the coordinates of the gophers; the following m lines give the coordinatesof the gopher holes. All distances are in metres; all times are in seconds; allvelocities are in metres per second.

输出

Output consists of a single line for each case, giving thenumber of vulnerable gophers.

样例输入

2 2 5 10
1.0 1.0
2.0 2.0
100.0 100.0
20.0 20.0

样例输出

1

来源

Waterloo local 2001.01.27

 -----------------------------------------------------

思路

把老鼠看做二分图的左半部,把地洞看做二分图的右半部,如果老鼠能跑进地洞就在老鼠和地洞之间连一条边,问题转化为求二分图的最大匹配。由于需要频繁查找一个节点的相邻节点,故用邻接表存储二分图。

用匈牙利算法求解,其中搜索增广道路用递归深搜实现。数组link表示节点的匹配关系,一个算例只有一个link数组;数组vis表示一次增广道路的求解过程中节点是否被访问,每次增广道路的求解过程vis都不同,需要每次清零

-----------------------------------------------------

代码

#include<iostream>
#include<fstream>
#include<vector>
#include<cstring>
using namespace std;

const int GMAX = 105;					// max(n)
const int NMAX = 205;					// max(m+n)
int n,m;
int link[NMAX] = {};					// 一个节点的匹配节点(n+m)个
bool vis[NMAX] = {};					// 该节点是否被访问过
vector<int> G[NMAX] = {};				// 邻接表
double g_x[GMAX] = {};					// n只老鼠的坐标
double g_y[GMAX] = {};
double h_x[GMAX] = {};					// m个地洞的坐标
double h_y[GMAX] = {};

int dfs(int i)							// 求从i开始的增广道路(递归),存在则返回1,不存在返回0
{
	vector<int> v = G[i];
	vector<int>::iterator it;
	int j;
	for (it=v.begin(); it!=v.end(); it++)			// 遍历i的相邻节点
	{
		j = *it;
		if (!vis[j])								// 如果j未标记
		{
			vis[j] = 1;								// 把j标记为已访问
			if (link[j]==-1 || dfs(link[j]))		// 如果j还没有匹配或存在从j出发的增广道路
			{
				link[j] = i;						// 更改j的后继节点为i
				return 1;							// 找到一条增广道路
			}
		}
	}
	return 0;										// 找不到增广道路
}


int main()
{
#ifndef ONLINE_JUDGE
	ifstream fin ("0403_1538.txt");
	int i,j;
	double s,v,rad,ans;
	while (fin >> n >> m)
	{
		fin >> s >> v;
		rad = s*v;
		memset(link, -1, sizeof(link));
		memset(vis, 0, sizeof(vis));
		for (i=0; i<NMAX; i++)
		{
			G[i].clear();
		}
		for (i=0; i<n; i++)
		{
			fin >> g_x[i] >> g_y[i];
		}
		for (i=0; i<m; i++)
		{
			fin >> h_x[i] >> h_y[i];
			for (j=0; j<n; j++)					// 求邻接表
			{
				if ((h_x[i]-g_x[j])*(h_x[i]-g_x[j])+(h_y[i]-g_y[j])*(h_y[i]-g_y[j])<=rad*rad)
				{
					G[n+i].push_back(j);
					G[j].push_back(n+i);
				}
			}
		}
		ans = 0;
		for (i=0; i<n; i++)
		{
			memset(vis, 0, sizeof(vis));		// 每次找增广道路之前把vis数组清零
			ans += dfs(i);						// 从i出发是否存在增广道路
		}
		cout << (n-ans) << endl;
	}
	fin.close();
#endif
#ifdef ONLINE_JUDGE
	int i,j;
	double s,v,rad,ans;
	while (cin >> n >> m)
	{
		cin >> s >> v;
		rad = s*v;
		memset(link, -1, sizeof(link));
		memset(vis, 0, sizeof(vis));
		for (i=0; i<NMAX; i++)
		{
			G[i].clear();
		}
		for (i=0; i<n; i++)
		{
			cin >> g_x[i] >> g_y[i];
		}
		for (i=0; i<m; i++)
		{
			cin >> h_x[i] >> h_y[i];
			for (j=0; j<n; j++)					// 求邻接表
			{
				if ((h_x[i]-g_x[j])*(h_x[i]-g_x[j])+(h_y[i]-g_y[j])*(h_y[i]-g_y[j])<=rad*rad)
				{
					G[n+i].push_back(j);
					G[j].push_back(n+i);
				}
			}
		}
		ans = 0;
		for (i=0; i<n; i++)
		{
			memset(vis, 0, sizeof(vis));		// 每次找增广道路之前把vis数组清零
			ans += dfs(i);						// 从i出发是否存在增广道路
		}
		cout << (n-ans) << endl;
	}
#endif
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值