HDU-2389 Rain on your Parade(二分图之Hopcroft-Karp算法)

84 篇文章 6 订阅
20 篇文章 1 订阅

题意:

N个人M把伞,T秒后下雨,给出每个人的坐标和跑路的速度以及伞的坐标,问在T秒内最多能有几个人能拿到伞(约束:每把伞只能一个人用)。(N,M <= 3000)

思路:

很容易看出来的最大匹配,但发现点的数量达到3000,而边的数量也随之达到3000*3000,而匈牙利算法的复杂度为O(N*M)[N是A部的点数,M是边数]。所以这里引出了一个复杂度为O(sqrt(N)*M)的Hopcroft-Karp算法,入门点击


代码(模板):

#include <algorithm>
#include <string.h>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 3005;
struct node
{
	int x, y, d;
} a[maxn];
vector<int> G[maxn];
int T, N, M;
int nx, ny;		//A部点数,B部点数 
int dx[maxn], dy[maxn], cx[maxn], cy[maxn];
//A部层次,B部层次,A部匹配的B部点,B部匹配的A部点 
int dis, vis[maxn];
queue<int> q;
bool BFS()
{
	memset(dx, -1, sizeof dx);
	memset(dy, -1, sizeof dy);
	while(!q.empty()) q.pop();
	dis = inf;
	for(int i = 1; i <= nx; ++i)
	if(cx[i] == -1) q.push(i);	//每次都以A部未匹配的点为起点 
	while(!q.empty())
	{
		int u = q.front(); q.pop();
		if(dx[u] > dis) break;	//寻诈增广路终止条件:第K层包含为匹配的B部顶点 
		for(int i = 0; i < G[u].size(); ++i)
		{
			int v = G[u][i];
			if(dy[v] == -1)
			{
				dy[v] = dx[u]+1;
				//确定当找到B部未匹配点时的层次 
				if(cy[v] == -1) dis = dy[v];
				else
				{
					//通过已匹配边直接寻到A部的点 
					dx[cy[v]] = dy[v]+1;
					q.push(cy[v]);
				}
			}
		}
	}
	return dis != inf;
}
int DFS(int cur)
{
	for(int i = 0; i < G[cur].size(); ++i)
	{
		int v = G[cur][i];
		if(!vis[v] && dy[v] == dx[cur]+1)
		{
			vis[v] = 1;
			//当第dis层的B部点不是未匹配点时,直接忽略(优化) 
			if(cy[v] != -1 && dy[v] == dis) continue;
			if(cy[v] == -1 || DFS(cy[v]))
			{
				cy[v] = cur;
				cx[cur] = v;
				return 1;
			}
		}
	}
	return 0;
}
int hopcroft_karp()
{
	memset(cx, -1, sizeof cx);
	memset(cy, -1, sizeof cy);
	int ans = 0;
	while(BFS())
	{
		memset(vis, 0, sizeof vis);
		for(int i = 1; i <= nx; ++i)
		if(cx[i] == -1) ans += DFS(i);
	}
	return ans;
}
int judge(int x1, int y1, int x2, int y2, int d)
{
	return (d*T*d*T) >= (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
void mapping()	//构图 
{
	int x, y;
	scanf("%d %d", &T, &M);
	for(int i = 1; i <= M; ++i)
	{
		G[i].clear();
		scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].d);
	}
	scanf("%d", &N);
	for(int i = 1; i <= N; ++i)
	{
		scanf("%d %d", &x, &y);
		for(int j = 1; j <= M; ++j)
		{
			if(judge(x, y, a[j].x, a[j].y, a[j].d))
			G[j].push_back(i);
		}
	}
}
int main()
{
	int tt; scanf("%d", &tt);
	for(int _ = 1; _ <= tt; ++_)
	{
		mapping();
		nx = M, ny = N;
		int ans = hopcroft_karp();
		printf("Scenario #%d:\n%d\n\n", _, ans);
	}
	return 0;
}

继续加油~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值