hdu5992 Finding Hotels(二分 || KDTree)

                                               Finding Hotels

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)
Total Submission(s): 1969    Accepted Submission(s): 623


 

Problem Description

There are N hotels all over the world. Each hotel has a location and a price. M guests want to find a hotel with an acceptable price and a minimum distance from their locations. The distances are measured in Euclidean metric.

 

 

Input

The first line is the number of test cases. For each test case, the first line contains two integers N (N ≤ 200000) and M (M ≤ 20000). Each of the following N lines describes a hotel with 3 integers x (1 ≤ x ≤ N), y (1 ≤ y ≤ N) and c (1 ≤ c ≤ N), in which x and y are the coordinates of the hotel, c is its price. It is guaranteed that each of the N hotels has distinct x, distinct y, and distinct c. Then each of the following M lines describes the query of a guest with 3 integers x (1 ≤ x ≤ N), y (1 ≤ y ≤ N) and c (1 ≤ c ≤ N), in which x and y are the coordinates of the guest, c is the maximum acceptable price of the guest.

 

 

Output

For each guests query, output the hotel that the price is acceptable and is nearest to the guests location. If there are multiple hotels with acceptable prices and minimum distances, output the first one.

 

 

Sample Input

2
3 3
1 1 1
3 2 3
2 3 2
2 2 1
2 2 2
2 2 3
5 5
1 4 4
2 1 2
4 5 3
5 2 1
3 3 5
3 3 1
3 3 2
3 3 3
3 3 4
3 3 5

Sample Output

1 1 1
2 3 2
3 2 3
5 2 1
2 1 2
2 1 2
1 4 4
3 3 5

 Source

2016ACM/ICPC亚洲区青岛站-重现赛(感谢中国石油大学)

 

 

Recommend

jiangzijing2015

 

一、原题地址

点我传送

 

二、题目大意

在二维坐标上有n个宾馆,m个客人,每个宾馆有自己的消费值,客人们也有自己的权值,他们只能住在价格小于权值的那些宾馆。要求每个客人住的距离靠宾馆尽可能的近,当存在多个距离相同的宾馆时,取id靠前的宾馆。

 

三、思路

宾馆按照x轴的坐标排序好,y轴先不管。每次读入客人的坐标和权值时,在二维平面内用二分找到这个客人x所在的位置,然后开始枚举他两旁的宾馆能否符合情况,在枚举的时候,可以先只考虑当前点到达客人的X轴上的距离xx,如果这个距离xx都已经大于当前我们更新的最小距离了,那么这个宾馆和后续的宾馆肯定都是不行的,而这也是这样做不会超时的关键,当我们发现两边的宾馆都不在考虑的范围内时,就可以退出枚举了。

之所以这样做,其实是因为不会KDtree.

2018/11/5补上了KDtree的做法。KDtree快速查找平面内最近的点,然后只要比较一下价格和编号的大小就好了。

 

四、代码

①二分做法

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
const int inf = (1 << 30) - 1;
const long long int INF = 1e18;
typedef long long LL;


int T, n, m;
struct Node
{
	LL x, y;
	int id, w;
}p[20005], h[200005];
bool cmp(Node a, Node b)
{
	if (a.x != b.x)return a.x < b.x;
	else
		return a.id < b.id;
}
int bir_sort(Node t)
{
	int l, r, mid, ans;
	l = 1, r = n ;
	while (l <= r)
	{
		mid = (l + r) / 2;
		if (h[mid].x > t.x)
		{
			r = mid - 1;
		}
		else
		{
			ans = mid;
			l = mid + 1;
		}
	}
	return ans;
}
void solve(Node t)
{
	double mindis = inf;
	int anspos, ansid;
	int st = bir_sort(t);//二分查找客人所在的X轴的相对位置
	bool tagleft = true, tagright = true;
	for (int i = 0; i < n; i++)
	{
		if (!tagleft&&!tagright)break;//左右两端都不行则退出试探
		int L = st - i, R = st + i;
		if (tagleft&&L >= 1)
		{
			double xx = sqrt(1.0*(t.x - h[L].x)*(t.x - h[L].x));
			if (xx > mindis)
				tagleft = false;
			else if(h[L].w<=t.w)
			{
				double nowdis = sqrt(1.0*(t.x - h[L].x)*(t.x - h[L].x)
					+ (t.y - h[L].y)*(t.y - h[L].y));
				if (nowdis < mindis)
				{
					mindis = nowdis; anspos = L; ansid = h[L].id;
				}
				else if (nowdis == mindis&&ansid > h[L].id)
				{
					mindis = nowdis; anspos = L; ansid = h[L].id;
				}
			}
		}
		//试探二分点的左侧
		if (tagright&&R <= n)
		{
			double xx = sqrt(1.0*(t.x - h[R].x)*(t.x - h[R].x));
			if (xx > mindis)
				tagright = false;
			else if (h[R].w <= t.w)
			{
				double nowdis = sqrt(1.0*(t.x - h[R].x)*(t.x - h[R].x)
					+ (t.y - h[R].y)*(t.y - h[R].y));
				if (nowdis < mindis)
				{
					mindis = nowdis; anspos = R; ansid = h[R].id;
				}
				else if (nowdis == mindis&&ansid > h[R].id)
				{
					mindis = nowdis; anspos = R; ansid = h[R].id;
				}
			}
		}
		//试探二分点的右侧
	}
	printf("%lld %lld %d\n", h[anspos].x, h[anspos].y, h[anspos].w);
}
int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d", &n, &m);
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld %lld %d", &h[i].x, &h[i].y, &h[i].w);
			h[i].id = i;
		}
		sort(h + 1, h + 1 + n, cmp);
		for (int i = 1; i <= m; i++)
		{
			scanf("%lld %lld %d", &p[i].x, &p[i].y, &p[i].w);
			solve(p[i]);
		}
	}
	getchar(); 
	getchar();
}

②KDtree做法

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<map>
#include<unordered_set>
#include<vector>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef long long LL;
const double eps = 1e-6;



#define N 200005
#define lson rt<<1
#define rson rt<<1|1
#define Pair pair<double ,Node>
#define Sqrt2(x) (x) * (x)
int n, k, idx;                                    //idx用以记录属性

struct Node
{
	LL feature[5];            //定义属性数组
	LL id;
	bool operator < (const Node &u)const
	{
		return feature[idx] < u.feature[idx];
	}
}_date[N];                    //_data[]数组代表输入的数据

priority_queue<Pair>Q;        //队列Q用于存放离p最近的m个数据

class KDTree
{
public:
	void Build(int, int, int, int);        //建树
	void Query(Node, int, int, int);    //查询
private:
	Node date[N << 2];        //data[]数组代表K-D树的所有节点数据
	int flag[N << 2];        //用于标记某个节点是否存在,1表示存在,-1表示不存在
}kd;


//建树步骤,参数dept代表树的深度
void KDTree::Build(int l, int r, int rt, int dept)
{
	if (l > r)return;
	flag[rt] = 1;                        //表示编号为rt的节点存在
	flag[lson] = flag[rson] = -1;        //当前节点的孩子暂时标记不存在
	idx = dept % 2;                        //按照编号为idx的属性进行划分
	int mid = (l + r) >> 1;
	nth_element(_date + l, _date + mid, _date + r + 1);     //nth_element()为STL中寻找中位数函数
	date[rt] = _date[mid];
	Build(l, mid - 1, lson, dept + 1);
	Build(mid + 1, r, rson, dept + 1);
}

//查询函数,寻找离p最近的m个特征属性
void KDTree::Query(Node p, int m, int rt, int dept)
{
	if (flag[rt] == -1)return;    //不存在的节点不遍历
	Pair cur(0, date[rt]);        //获取当前节点的数据和到p的距离
	for (int i = 0; i < 2; i++)
		cur.first += Sqrt2(cur.second.feature[i] - p.feature[i]);
	int dim = dept % 2;            //跟建树一样,这样能保证相同节点的dim值不变
	bool fg = 0;                //用于标记是否需要遍历右子树
	int x = lson, y = rson;
	if (p.feature[dim] >= date[rt].feature[dim])
		swap(x, y);                //数据p的第dim个特征值大于等于当前的数据,则需要进入右子树
	if (~flag[x])Query(p, m, x, dept + 1);//如果节点x存在,则进入子树继续遍历

										  //以下是回溯过程,维护一个优先队列
	if (Q.size() < m)        //如果队列没有满,则继续放入
	{
		if (p.feature[2] >= cur.second.feature[2])
		{
			Q.push(cur);
		}
		fg = 1;
	}
	else
	{
		if (p.feature[2] >= cur.second.feature[2] && (cur.first < Q.top().first || (cur.first == Q.top().first&&cur.second.id<Q.top().second.id)))//如果找到更小的距离,则用于替换队列Q中最大的距离的数据
		{
			Q.pop();
			Q.push(cur);
		}
		if (Sqrt2(p.feature[dim] - date[rt].feature[dim]) < Q.top().first)
		{
			fg = 1;
		}
	}
	if (~flag[y] && fg)
		Query(p, m, y, dept + 1);
}


Node ans[20];
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d", &n, &k);
		for (int i = 0; i< n; i++)
		{
			for (int j = 0; j < 3; j++)
				scanf("%lld", &_date[i].feature[j]);
			_date[i].id = i;
		}
		kd.Build(0, n - 1, 1, 0);
		while (k--)
		{
			Node que;
			for (int i = 0; i < 3; i++)
				scanf("%lld", &que.feature[i]);
			kd.Query(que, 1, 1, 0);
			while (!Q.empty())
			{
				Node t = Q.top().second;
				Q.pop();
				printf("%lld %lld %lld\n", t.feature[0], t.feature[1], t.feature[2]);
			}
		}
	}

	getchar();
	getchar();
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值