Rain on your Parade——二分图匹配Hopcroft-Karp算法

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2389

You’re giving a party in the garden of your villa by the sea. The party is a huge success, and everyone is here. It’s a warm, sunny evening, and a soothing wind sends fresh, salty air from the sea. The evening is progressing just as you had imagined. It could be the perfect end of a beautiful day.
But nothing ever is perfect. One of your guests works in weather forecasting. He suddenly yells, “I know that breeze! It means its going to rain heavily in just a few minutes!” Your guests all wear their best dresses and really would not like to get wet, hence they stand terrified when hearing the bad news.
You have prepared a few umbrellas which can protect a few of your guests. The umbrellas are small, and since your guests are all slightly snobbish, no guest will share an umbrella with other guests. The umbrellas are spread across your (gigantic) garden, just like your guests. To complicate matters even more, some of your guests can’t run as fast as the others.
Can you help your guests so that as many as possible find an umbrella before it starts to pour?

Given the positions and speeds of all your guests, the positions of the umbrellas, and the time until it starts to rain, find out how many of your guests can at most reach an umbrella. Two guests do not want to share an umbrella, however.

Input

The input starts with a line containing a single integer, the number of test cases.
Each test case starts with a line containing the time t in minutes until it will start to rain (1 <=t <= 5). The next line contains the number of guests m (1 <= m <= 3000), followed by m lines containing x- and y-coordinates as well as the speed si in units per minute (1 <= s i <= 3000) of the guest as integers, separated by spaces. After the guests, a single line contains n (1 <= n <= 3000), the number of umbrellas, followed by n lines containing the integer coordinates of each umbrella, separated by a space.
The absolute value of all coordinates is less than 10000.

Output

For each test case, write a line containing “Scenario #i:”, where i is the number of the test case starting at 1. Then, write a single line that contains the number of guests that can at most reach an umbrella before it starts to rain. Terminate every test case with a blank line.

Sample Input

2
1
2
1 0 3
3 0 3
2
4 0
6 0
1
2
1 1 2
3 3 2
2
2 2
4 4

Sample Output

Scenario #1:
2

Scenario #2:
2

题目翻译:

你在海边的别墅的花园里举行聚会。聚会取得了巨大的成功,每个人都在这里。这是一个温暖、阳光明媚的夜晚,舒缓的风从海上散发出清新、咸气的空气。夜晚正如你想象的那样在前进。这可能是美好一天的完美结局。‎
‎但没有什么是完美的。您的一位客人从事天气预报工作。他突然喊道:"我知道那微风!这意味着它要在几分钟内下大雨!你的客人都穿着他们最好的衣服,真的不想弄湿,所以他们站在害怕,当听到坏消息。‎
‎你准备了几把雨伞,可以保护你的几个客人。雨伞很小,由于客人都略带势利,因此没有客人会与其他客人共用雨伞。雨伞铺在你的(巨大的)花园,就像你的客人一样。使事情更加复杂的是,你的一些客人不能像其他人那样跑得快。‎
‎你能帮你的客人,让尽可能多的人找到一把伞之前,它开始倒?‎
‎考虑到所有客人的位置和速度、雨伞的位置以及开始下雨的时间,了解您的客人最多能达到多少‎
‎人。然而,两位客人不想共用一把雨伞。‎

‎输入‎

‎输入以包含单个整数的行开始,即测试用例数。‎
‎每个测试用例都以一行包含时间 t(以分钟)开始,直到开始下雨(1 <\t <= 5)。下一行包含来宾数 m (1 <= m <= 3000),后跟包含 x 和 y 坐标的 m 线以及以每分钟单位为单位的速度 si(1 <= ‎‎i‎‎ <= 3000)作为整数,由空格分隔。在来宾之后,一行包含 n (1 <= n <= 3000),伞数,后跟 n 行包含每个伞的整数坐标,由空格分隔。‎
‎所有坐标的绝对值小于 10000。‎

‎输出‎

‎对于每个测试用例,编写一行包含"场景#i:",其中 i 是测试用例的编号,从 1 开始。然后,写一行,其中包含最多可以在下雨前到达雨伞的客人人数。用空行终止每个测试用例。

 

其实这个题的难点不在于怎样建二分图,而在于要用HK(O(sqrt(n)*m)算法,而不能用直接DFS(O(n*m))来求最大匹配。

先讲一下怎么建图,我们知道来宾数就是X点集,那么雨伞数就是Y点集,如果来宾可以在规定的时间内跑到有雨伞的地方,那么这两个点就连一条边。

其实HK算法就是先用BFS预先求出一些不相交的最短增广路,形成极大增广路集,然后在对这些增广路进行增广,而不只是单纯的一个一个增广。

具体实现看代码!

 

#include 
#include
#include
#include
#include
using namespace std;
const int maxn=3e3+7;
const int INF =0x3f3f3f3f;
int G[maxn][maxn],visited[maxn];
int cx[maxn],cy[maxn];//标记分别与x,y匹配的点。 
int dx[maxn],dy[maxn];//存储BFS时x,y的层数 
int n,m,dis;
struct people{
	int x,y;
	int v;
}p[maxn];
struct umbrella{
	int x,y;
}u[maxn];
int bfs(){
	queue q;
	dis=INF;
	memset(dx,-1,sizeof(dx));
	memset(dy,-1,sizeof(dy));// dx、dy用来标记当前点是否在一个交替路中
	for(int i=1;i<=m;i++){
		if(cx[i]==-1){//初始化x点集中没有匹配的点入队 
			q.push(i);
			dx[i]=0;
		}
	}
	while(!q.empty()){//BFS搜出目前最短的增广路集合 
		int u=q.front();
		q.pop();
		if(dx[u]>dis) break;//已经找到最短增广路,跳出 
		for(int v=1;v<=n;v++){
			if(G[u][v]&&dy[v]==-1){//如果u与v有边而且v没有被搜到 
				dy[v]=dx[u]+1;//v在一条交替路上 
				if(cy[v]==-1) dis=dy[v];//如果当前点是未匹配点,那么该交替路为增广路 
				else{//否则把当前节点入队,层数加一 
					dx[cy[v]]=dy[v]+1;
					q.push(cy[v]);
				}
			}
		}
	}
	if(dis==INF) return 0;//如果不存在增广路,那么满足最大匹配。 
	else return 1;
}
int find(int x)//类似经典的最大匹配写法 
{
	for(int i = 1;i<=n;++i){
		if(!visited[i]&&G[x][i]&&dy[i]==dx[x]+1){//i在一条交替路上 
			visited[i]=1;
			if(cy[i]!=-1&&dy[i]==dis) continue;//如果该点已经匹配,而且不满足最短增广路,就跳出 
			if(cy[i]==-1||find(cy[i])){
				cy[i]=x;
				cx[x]=i;
				return 1;
			}
		}
	}
	return 0;
}
int match()
{
	memset(cx,-1,sizeof(cx));
	memset(cy,-1,sizeof(cy));
	int ans=0;
	while(bfs()){//每次bfs求出一些不相交的最短增广路 
		memset(visited,0,sizeof(visited));
		for(int i = 1;i<=m;++i)
			if(cx[i]==-1&&find(i))//如果i点还没有匹配,进行匹配 
				ans++; 
		
	} 
	return ans;
}
double dist(int i,int j){
	return sqrt((p[i].x-u[j].x)*(p[i].x-u[j].x)+(p[i].y-u[j].y)*(p[i].y-u[j].y));
}
int main(int argc, char** argv) {
	int ncase;
	scanf("%d",&ncase);
	for(int t = 1;t<=ncase;++t){
		memset(G,0,sizeof(G));
		int time;
		scanf("%d %d",&time,&m);
		for(int i = 1;i<=m;++i)
			scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].v);
	    scanf("%d",&n);
		for(int i = 1;i<=n;++i)
		    scanf("%d%d",&u[i].x,&u[i].y);
		for(int i = 1;i<=m;++i){
			int r=p[i].v*time;
			for(int j = 1;j<=n;j++)
				if(dist(i,j)<=r)//建二分图 
				   G[i][j]=1;
		}
		printf("Scenario #%d:\n",t);
		printf("%d\n\n",match());
	}
	return 0;
}

有关二分图的匹配可以参考我的这篇文章:https://blog.csdn.net/qq_43472263/article/details/96831025

你可能感兴趣的:(C++算法入门,数据结构,二分图匹配)