• 【54.08%】【BZOJ 1941】Hide and Seek


    Time Limit: 16 Sec  Memory Limit: 162 MB
    Submit: 919  Solved: 497
    [Submit][Status][Discuss]

    Description

    小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏---捉迷藏。 但是,他们觉得,玩普通的捉迷藏没什么意思,还是不够寂寞,于是,他们决定玩寂寞无比的螃蟹版捉迷藏,顾名思义,就是说他们在玩游戏的时候只能沿水平或垂直方向走。一番寂寞的剪刀石头布后,他们决定iPig去捉giPi。由于他们都很熟悉PKU的地形了,所以giPi只会躲在PKU内n个隐秘地点,显然iPig也只会在那n个地点内找giPi。游戏一开始,他们选定一个地点,iPig保持不动,然后giPi用30秒的时间逃离现场(显然,giPi不会呆在原地)。然后iPig会随机地去找giPi,直到找到为止。由于iPig很懒,所以他到总是走最短的路径,而且,他选择起始点不是随便选的,他想找一个地点,使得该地点到最远的地点和最近的地点的距离差最小。iPig现在想知道这个距离差最小是多少。 由于iPig现在手上没有电脑,所以不能编程解决这个如此简单的问题,所以他马上打了个电话,要求你帮他解决这个问题。iPig告诉了你PKU的n个隐秘地点的坐标,请你编程求出iPig的问题。

    Input

    第一行输入一个整数N 第2~N+1行,每行两个整数X,Y,表示第i个地点的坐标

    Output

    一个整数,为距离差的最小值。

    Sample Input

    4
    0 0
    1 0
    0 1
    1 1

    Sample Output

    1

    HINT

    对于30%的数据,N<=1000 对于100%的数据,N<=500000,0<=X,Y<=10^8 保证数据没有重点保证N>=2

    Source

    【题解】

    要用KD-tree来做。

    如果想学习KD-tree。可以看下这篇我转载的文章

    http://blog.csdn.net/harlow_cheng/article/details/52535535

    这篇文章用了图片的例子来讲。我觉得挺不错的。

    然后程序里面也有适当的注解。

    回归这题。

    枚举每个点。然后找一个距离该点最远的点。找一个离该点最近的点(除了本身)

    然后相减。

    (1..n)取min即可。

    发现网上的题解都很豪放啊。都不按套路出牌。不是说好的要按照方差来划分的吗????我看到的程序都是按照x和y轮流划分。。

    就是0,1,0,1,0,1这样的。。

    研究了好久的程序啊。

    我说下这题的思路吧。

    K-D树不是把“区间”里面的点都划分成左右两部分吗。

    我们就用一个ma_x[2],mi_n[2] 来维护这个每个子区间的左上角和右下角的坐标。

    然后原本最朴素的是按照方差的划分依据往左往右。

    现在我们用两个估价函数就行了。

    gujia_max

    gujia_min

    分别表示往某个方向走最大的曼哈顿距离。

    我相信gujia_max都能看得懂的。

    我就只讲下gujia_min

    这是代码块。

    int gujia_min(int rt)
    {
    	int temp = 0;
    	for (int i = 0; i <= 1; i++)
    	{
    		temp += max(0, op.d[i] - t[rt].ma_x[i]);
    		temp += max(0, t[rt].mi_n[i]- op.d[i]);
    	}
    	return temp;
    }
    其中的op是当前在处理的点。

    然后我们用mi_n和max_存储的可以理解为某个子树区域里面点形成的最大矩形。

    这个函数可以判断我们当前在处理的点是否在矩形内。

    如果在矩形内就返回0.

    表示估价函数最小(因为在矩形内,所以“认为这个点极有可能在那个区间内”);

    如果不在那个矩形内。就返回到达这个矩形边缘所需要的最小曼哈顿距离。

    我想之所以说它是估价。就是因为没办法确定吧。记住这种方法就行啦。

    ps:关于如何排除找到的点不是自身的方法。在更新答案的时候。看一下要更新的答案是不是会变成0(因为没有重点,所以如果距离是0,那肯定就是重复的了,则不要更新答案就好了);

    【代码】绝对不是网上看到的那种牛逼哄哄的写了一大串结构体的代码:)

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MAXN = 501000;
    const int INF = 2100000000;
    
    struct point
    {
    	int d[2], ma_x[2], mi_n[2], l, r;
    };
    
    point t[MAXN],p[MAXN],op; //t是存储kd树的.p则是用于nth_element的排序
    int x[MAXN], y[MAXN],n,root,now,temp1,temp2; //now是当前划分的依据
    
    bool cmp(point a, point b) 
    {
    	if (a.d[now] < b.d[now])
    		return true;
    	return false;
    }
    
    void push_up(int rt) //更新最大矩形的左上角和右下角。(不一定有这两个点)
    {
    	int l = t[rt].l, r = t[rt].r;
    	for (int i = 0; i <= 1; i++)
    	{
    		if (l)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[l].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[l].mi_n[i]);
    		}
    		if (r)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[r].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[r].mi_n[i]);
    		}
    	}
    }
    
    int build(int begin, int end, int fx)
    {
    	int m = (begin + end) >> 1;
    	now = fx;
    	nth_element(p + begin, p + m, p + end + 1, cmp);  //左闭右开
    	t[m] = p[m];
    	for (int i = 0; i <= 1; i++)
    		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
    	if (begin < m)
    		t[m].l = build(begin, m - 1, 1 - fx);
    	if (m < end)
    		t[m].r = build(m + 1, end, 1 - fx);
    	push_up(m);
    	return m;
    }
    
    void input_data()
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++)
    	{
    		scanf("%d%d", &x[i], &y[i]);
    		p[i].d[0] = x[i];
    		p[i].d[1] = y[i];
    	}
    	root = build(1, n, 0);
    }
    
    int get_dis(point a, point b)
    {
    	return abs(a.d[0] - b.d[0]) + abs(a.d[1] - b.d[1]);
    }
    
    int gujia_max(int rt)
    {
    	int temp = 0;
    	for (int i = 0; i <= 1; i++)
    		temp += max(abs(op.d[i] - t[rt].ma_x[i]), abs(op.d[i] - t[rt].mi_n[i]));
    	return temp;
    }
    
    void query_max(int rt)
    {
    	int dis = get_dis(op, t[rt]);
    	temp1 = max(dis, temp1);
    	int gl = -INF, gr = -INF;
    	int l = t[rt].l, r = t[rt].r;
    	if (l)
    		gl = gujia_max(l);
    	if (r)
    		gr = gujia_max(r);
    	if (gl > gr) //并不是说估价完就指定了一个方向!
    	{
    		if (gl > temp1)
    			query_max(l);
    		if (gr > temp1)
    			query_max(r);
    	}
    	else
    	{
    		if (gr > temp1)
    			query_max(r);
    		if (gl > temp1)
    			query_max(l);
    	}
    }
    
    int gujia_min(int rt)
    {
    	int temp = 0;
    	for (int i = 0; i <= 1; i++)
    	{
    		temp += max(0, op.d[i] - t[rt].ma_x[i]);
    		temp += max(0, t[rt].mi_n[i]- op.d[i]);
    	}
    	return temp;
    }
    
    void query_min(int rt)
    {
    	int dis = get_dis(op, t[rt]);
    	if (dis) //这是排除是本身这个点的方法 即判断距离是否为0;为0就不更新
    		temp2 = min(temp2, dis);
    	int l = t[rt].l, r = t[rt].r;
    	int gl = INF, gr = INF;
    	if (l)
    		gl = gujia_min(l);
    	if (r)
    		gr = gujia_min(r);
    	if (gl < gr)
    	{
    		if (gl < temp2)
    			query_min(l);
    		if (gr < temp2)
    			query_min(r);
    	}
    	else
    	{
    		if (gr < temp2)
    			query_min(r);
    		if (gl < temp1)
    			query_min(l);
    	}
    }
    
    void get_ans()
    {
    	int ans = INF;
    	for (int i = 1; i <= n; i++)
    	{
    		op.d[0] = x[i];
    		op.d[1] = y[i];
    		temp1 = -INF;
    		query_max(root);
    		temp2 = INF;
    		query_min(root);
    		ans = min(ans, temp1 - temp2);
    	}
    	printf("%d
    ", ans);
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	input_data();
    	get_ans();
    	return 0;
    }




  • 相关阅读:
    Teacher Bo HDU 5762(暴力)
    The Unique MST POJ1679(次小生成树)
    Sqrt Bo hdu 5752
    Borg Maze POJ 3026(BFS+最小生成树)
    Highways POJ 1751(最小生成树)
    hdu---2050---折线分割平面
    POj---1469---Courses
    poj---2349---Arctic Network
    poj-2528-Mayor's posters
    POJ---3468---A Simple Problem with Integers
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632253.html
Copyright © 2020-2023  润新知