HDU 2389 Rain on your Parade / HUST 1164 4 Rain on your Parade(二分图的最大匹配)
HDU 2389 Rain on your Parade / HUST 1164 4 Rain on your Parade(二分图的最大匹配)
Description
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
Http
HDU:https://vjudge.net/problem/HDU-2389
HUSTT:https://vjudge.net/problem/HUST-1164
Source
二分图的最大匹配
题目大意
现在马上要下雨了,给出n个人和m把雨伞的坐标位置,以及每个人的奔跑速度,还有距离下雨的时间。每把雨伞只能供一人使用。求在下雨前最多有多少人可以拿到雨伞。
解决思路
这个题目一看就是二分图的最大匹配问题。但若直接把匈牙利算法往上一摆,会TLE。
为什么呢?好好看题:n,m<=3000。而原来我们做的题目都是n,m<=100或<=200的。所以这道题不能直接用匈牙利算法解决。
所以这道题我们要用到匈牙利算法的改进版:Hopcroft-Karp算法(一下简称HK算法)
我们知道,匈牙利算法时间效率不高的原因就是在一次匹配时只走一条增广边,每次都要重新找增广边,所以时间效率低。
那么,HK算法就是在每一次判断前,先用一个bfs预处理处理出多条增广边,下面先给出代码。
代码中的变量说明:
Dist:一个全局变量,保存当前最长的增广路的深度,方便及时退出bfs,同时也方便后面再进行Hungary时及时退出(后面会讲到)
Deep_x:人对应的点的深度(也就是到此点时的增广路长度),同时可以方便判断人i是否在这次bfs中走过了。这个变量在匈牙利算法中也会用到(后面会说到)
Deep_y:伞对应的点的深度(同上),也能判断伞是否在这次bfs中是否用过
Match_x:人所匹配的伞的编号
Match_y:伞所匹配的人的编号
E:从人->伞的边
其他变量的意义与题目一致
bool bfs()
{
queue<int> Q;//bfs要用的队列
while (!Q.empty())
Q.pop();
Dist=inf;//先置为inf
mem(Deep_x,-1);//先置为-1,表示还未经过
mem(Deep_y,-1);
for (int i=1;i<=m;i++)
if (Match_x[i]==-1)//若人i还未匹配,则将其加入队列(因为增广路的开始点是未匹配点)
{
Deep_x[i]=0;
Q.push(i);
}
while (!Q.empty())//因为有可能所有人都已经匹配,所以要把while判断写前面,以免出现RE
{
int u=Q.front();
Q.pop();
if (Deep_x[u]>Dist)//若发现当前选出的人u的深度已经大于能形成增广路的最大深度了,直接退出。这是一个很好的剪枝
break;
for (int i=0;i<E[u].size();i++)
{
int v=E[u][i];//选择伞v
if (Deep_y[v]==-1)//若伞v在这一次bfs中还未走过
{
Deep_y[v]=Deep_x[u]+1;//首先更新伞v的深度,同时表示已经处理过伞v了
if (Match_y[v]==-1)//当伞v还未匹配时,说明找到了一条增广路,这时更新最长增广路的值Dist。因为是bfs,所以可以保证越在后面出来的Deep值越大,所以不用加max
{
Dist=Deep_y[v];
}
else//如果伞v已经匹配,则处理其对应的匹配的人,并将这个人加入队列
{
Deep_x[Match_y[v]]=Deep_y[v]+1;
Q.push(Match_y[v]);
}
}
}
}
//cout<<"Dist "<<Dist<<endl;
if (Dist==inf)//这表示没有增广路了,说明算法执行完毕,返回0
return 0;
return 1;
}
总计一下,这个bfs过程的意义就是增广多条增广路,方便后面匈牙利算法。
那么接下来就是修改匈牙利算法了。还是先给出代码。
bool Hungary(int u)
{
for (int i=0;i<E[u].size();i++)
{
int v=E[u][i];
if ((vis[v]==0)&&(Deep_x[u]+1==Deep_y[v]))//注意,这里用到了bfs计算出来的Deep数组,这可以用来判断当前选的伞v是否是我们在dfs中选的那条增广路,有效剪枝
{
vis[v]=1;
if ((Match_y[v]!=-1)&&(Deep_y[v]==Dist))//若发现当前伞v的Deep已经和最大增广路长度一样,并且伞v已匹配,那么再递归向下找是无意义的,直接continue
continue;
if ((Match_y[v]==-1)||(Hungary(Match_y[v])))
{
Match_x[u]=v;//注意,因为添加了人对应匹配的伞这个变量,所以也要更新(这在之前的匈牙利中是没有的)
Match_y[v]=u;
return 1;
}
}
}
return 0;
}
最后总计一下HK算法的主要流程就是:
先找出多条不相交的增广路。
用匈牙利算法进行匹配。
(如有不正确之处还请各路神犇指教)
代码(后面附上了TLE的纯匈牙利算法)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define mem(Arr,x) memset(Arr,x,sizeof(Arr))
const int maxN=4000;
const int inf=2147483647;
int n,m;
int Time;//距离下雨的时间
int Dist;//下面这些变量的意义在上文已经讲过啦
vector<int> E[maxN];
int Match_x[maxN];
int Match_y[maxN];
bool vis[maxN];
int Deep_x[maxN];
int Deep_y[maxN];
int X[maxN];//人的X坐标
int Y[maxN];
int Speed[maxN];//人的速度
int read();
bool Judge(int u,int v,int num);
bool bfs();
bool Hungary(int u);
int main()
{
int T;
T=read();
for (int ti=1;ti<=T;ti++)
{
Time=read();
m=read();
for (int i=1;i<=m;i++)
E[i].clear();
for (int i=1;i<=m;i++)
{
X[i]=read();
Y[i]=read();
Speed[i]=read();
}
n=read();
for (int i=1;i<=n;i++)
{
int x,y;
x=read();
y=read();
for (int j=1;j<=m;j++)
if (Judge(x,y,j))
{
E[j].push_back(i);
}
}
int Ans=0;
mem(Match_x,-1);
mem(Match_y,-1);
while (bfs())//HK算法
{
mem(vis,0);
for (int i=1;i<=m;i++)
if (Match_x[i]==-1)
if (Hungary(i))
Ans++;
}
printf("Scenario #%d:\n%d\n\n",ti,Ans);
}
return 0;
}
int read()//读入优化
{
int x=0;
int k=1;
char ch=getchar();
while (((ch<'0')||(ch>'9'))&&(ch!='-'))
ch=getchar();
if (ch=='-')
{
k=-1;
ch=getchar();
}
while ((ch<='9')&&(ch>='0'))
{
x=x*10+ch-48;
ch=getchar();
}
return x*k;
}
bool Judge(int u,int v,int num)//判断某个人是否可以跑到某把伞
{
long long Dist=(long long)((X[num]-u))*(X[num]-u)+(long long)((Y[num]-v))*(Y[num]-v);
long long D=(long long)(Time)*Speed[num];
if (D*D>=Dist)
return 1;
return 0;
}
bool bfs()
{
queue<int> Q;//bfs要用的队列
while (!Q.empty())
Q.pop();
Dist=inf;//先置为inf
mem(Deep_x,-1);//先置为-1,表示还未经过
mem(Deep_y,-1);
for (int i=1;i<=m;i++)
if (Match_x[i]==-1)//若人i还未匹配,则将其加入队列(因为增广路的开始点是未匹配点)
{
Deep_x[i]=0;
Q.push(i);
}
while (!Q.empty())//因为有可能所有人都已经匹配,所以要把while判断写前面,以免出现RE
{
int u=Q.front();
Q.pop();
if (Deep_x[u]>Dist)//若发现当前选出的人u的深度已经大于能形成增广路的最大深度了,直接退出。这是一个很好的剪枝
break;
for (int i=0;i<E[u].size();i++)
{
int v=E[u][i];//选择伞v
if (Deep_y[v]==-1)//若伞v在这一次bfs中还未走过
{
Deep_y[v]=Deep_x[u]+1;//首先更新伞v的深度,同时表示已经处理过伞v了
if (Match_y[v]==-1)//当伞v还未匹配时,说明找到了一条增广路,这时更新最长增广路的值Dist。因为是bfs,所以可以保证越在后面出来的Deep值越大,所以不用加max
{
Dist=Deep_y[v];
}
else//如果伞v已经匹配,则处理其对应的匹配的人,并将这个人加入队列
{
Deep_x[Match_y[v]]=Deep_y[v]+1;
Q.push(Match_y[v]);
}
}
}
}
//cout<<"Dist "<<Dist<<endl;
if (Dist==inf)//这表示没有增广路了,说明算法执行完毕,返回0
return 0;
return 1;
}
bool Hungary(int u)
{
for (int i=0;i<E[u].size();i++)
{
int v=E[u][i];
if ((vis[v]==0)&&(Deep_x[u]+1==Deep_y[v]))//注意,这里用到了bfs计算出来的Deep数组,这可以用来判断当前选的伞v是否是我们在dfs中选的那条增广路,有效剪枝
{
vis[v]=1;
if ((Match_y[v]!=-1)&&(Deep_y[v]==Dist))//若发现当前伞v的Deep已经和最大增广路长度一样,并且伞v已匹配,那么再递归向下找是无意义的,直接continue
continue;
if ((Match_y[v]==-1)||(Hungary(Match_y[v])))
{
Match_x[u]=v;//注意,因为添加了人对应匹配的伞这个变量,所以也要更新(这在之前的匈牙利中是没有的)
Match_y[v]=u;
return 1;
}
}
}
return 0;
}
/* 匈牙利算法,TLE
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxN=4000;
const int inf=2147483647;
int n,m;
vector<int> E[maxN];
bool vis[maxN];
int Match[maxN];
int GuestX[maxN];
int GuestY[maxN];
int Speed[maxN];
int read();
bool Hungary(int u);
int main()
{
int T;
int Time;
cin>>T;
for (int ti=1;ti<=T;ti++)
{
cin>>Time;
cin>>m;
for (int i=1;i<=m;i++)
E[i].clear();
for (int i=1;i<=m;i++)
{
GuestX[i]=read();
GuestY[i]=read();
Speed[i]=read();
Speed[i]=Speed[i]*Time;
}
int x,y;
cin>>n;
for (int i=1;i<=n;i++)
{
x=read();
y=read();
for (int j=1;j<=m;j++)
{
int Dist=(x-GuestX[j])*(x-GuestX[j])+(y-GuestY[j])*(y-GuestY[j]);
if (Speed[i]*Speed[i]>=Dist)
E[j].push_back(i);
}
}
int Ans=0;
memset(Match,-1,sizeof(Match));
for (int i=1;i<=m;i++)
{
memset(vis,0,sizeof(vis));
if (Hungary(i))
{
Ans++;
}
}
printf("Scenario #%d:\n%d\n\n",ti,Ans);
//cout<<Ans<<endl;
}
}
int read()
{
int x=0;
int k=1;
char ch=getchar();
while (((ch<'0')||(ch>'9'))&&(ch!='-'))
ch=getchar();
if (ch=='-')
{
k=-1;
ch=getchar();
}
while ((ch<='9')&&(ch>='0'))
{
x=x*10+ch-48;
ch=getchar();
}
return x*k;
}
bool Hungary(int u)
{
for (int i=0;i<E[u].size();i++)
{
int v=E[u][i];
if (vis[v]==0)
{
vis[v]=1;
if ((Match[v]==-1)||(Hungary(Match[v])))
{
Match[v]=u;
return 1;
}
}
}
return 0;
}
*/
HDU 2389 Rain on your Parade / HUST 1164 4 Rain on your Parade(二分图的最大匹配)的更多相关文章
- HDU 2389 ——Rain on your Parade——————【Hopcroft-Karp求最大匹配、sqrt(n)*e复杂度】
Rain on your Parade Time Limit:3000MS Memory Limit:165535KB 64bit IO Format:%I64d & %I64 ...
- HDU 2444 The Accomodation of Students 二分图判定+最大匹配
题目来源:HDU 2444 The Accomodation of Students 题意:n个人能否够分成2组 每组的人不能相互认识 就是二分图判定 能够分成2组 每组选一个2个人认识能够去一个双人 ...
- HDU 2389 Rain on your Parade(二分匹配,Hopcroft-Carp算法)
Rain on your Parade Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 655350/165535 K (Java/Ot ...
- (匹配 Hopcroft-Karp算法)Rain on your Parade -- Hdu --2389
链接: http://acm.hdu.edu.cn/showproblem.php?pid=2389 不能用匈牙利,会TEL的,用Hopcroft-Karp Hopcroft-Karp课件 以前是寻找 ...
- HDU 2389 Rain on your Parade
大意:在一个二维坐标系上有nx个人和ny把伞,每个人都有自己的移动速度,问有多少人可以再 time 时间内移动到不同的雨伞处(不允许两个人共用一把伞). 输入数据: 第一行是一个T代表T组测试数据 ...
- F - Rain on your Parade - hdu 2389(二分图匹配,Hk算法)
题意:给一些人和一些伞的坐标,然后每个人都有一定的速度,还有多少时间就会下雨,问最多能有多少人可以拿到伞. 分析:题意很明确,可以用每个人和伞判断一下是否能够达到,如果能就建立一个联系.不过这道题的数 ...
- HDU 2389 Rain on your Parade 最大匹配(模板题)【HK算法】
<题目链接> 题目大意:有m个宾客,n把雨伞,预计时间t后将会下大雨,告诉你每个宾客的位置和速度,每把雨伞的位置,问你最多几个宾客能够拿到伞. 解题分析: 本题就是要我们求人与伞之间的最大 ...
- Rain on your Parade HDU - 2389 (hc板题)
在客人能够拿到的伞与客人之间建边 跑hc就好了.... 看看别人的:https://blog.csdn.net/wall_f/article/details/8248350 #include < ...
- Hdu 2389 二分匹配
题目链接 Rain on your Parade Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 655350/165535 K (Ja ...
随机推荐
- 深度解析PHP数组函数array_combine
前些天写了一篇关于array_merge的函数解析. 今天来看一个新的函数array_combine() 此函数一共有两个参数,一个是合并后数组的键名,另一个为键值. 注意:合并后数组的键名放在第一个 ...
- Scrapy的debug方式
Scrapy不方便调试,但是为了深入学习框架内部的一些原理,有时候仅仅依靠日志是不够的.下面提供一种scrapy的debug方式 demo直接用来自官方例子来演示:https://github.com ...
- 克隆 CentOS 后系统启动问题解析
问题背景: 虚拟机:VMware Workstation. 操作系统:CentOS,是Linux发行版之一. 问题: 原因:虚拟机的服务没有开启(虚拟机开启一般比较占内存,所有多数人都设置成手动开启) ...
- 架构师之路--应用架构的选型和dubbo
这个选型主要决定于系统复杂度.先回顾一下. 1>单一应用架构:对于一个流量很小的网站来说,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本.之前在上家公司做过一个微信公众号的开发就是基 ...
- 织梦CMS去广告方法 for DedeCMS V5.7
DedeCms 5.7新版发布,下来上传至服务器安装完毕,点击进入后台登陆界面,怎么多了广告链,而且登陆界面也变了,以前可不带这样的啊.按步骤一步一步来去版权再去广告吧. 一,去处后台登陆页login ...
- 使用外部容器运行spring-boot项目:不使用spring-boot内置容器让spring-boot项目运行在外部tomcat容器中
前言:本项目基于maven构建 spring-boot项目可以快速构建web应用,其内置的tomcat容器也十分方便我们的测试运行: spring-boot项目需要部署在外部容器中的时候,spring ...
- git初步用法
三. Gerrit的注册及使用 1. 简介 Gerrit为代码审核工具,git提交的代码,必须经过审核才能合入到正式的版本库中. 2. 注册步骤 (1) ...
- 【webpack整理】一、安装、配置、按需加载
如果你: 是前端热爱者 :) 有JavaScript/nodejs基础 会使用一些常用命令行,mkdir,cd,etc. 会使用npm 想对webpack有更深的认识,或许此时你恰好遇到关于webpa ...
- 面向面试编程——javascript对象的几种创建方式
javascript对象的几种创建方式 总共有以下几个模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.混合构造函数和原型模式 5.动态原型模式 6.寄生构造函数模式 7.稳妥构造函数模式 1 ...
- ecshop收货地址货到付款修改
用户选择某些地址时,支付方式里则可以依据此地址来对货到付款选项进行显示或隐藏.目前仅与顺丰合作,以顺丰提供的数据为准. 使用到的数据库分别如下: ecs_region//地方数据,PRIMARY KE ...