Mines【HDU - 4400】【离散化+BFS+STL】

19 篇文章 0 订阅
14 篇文章 0 订阅

题目链接

  思路爆炸!!!这道题简直了,我写这片文章给自己理清思路,也给读者一个清晰的写法。

 

思路

  很明显的离散化,点少、值大,所以用了离散化,怎么离散化呢??我的想法就是对于x我们进行离散化,然后只需要去找符合条件的y即可确定爆炸与否(题目还是人性化的给的是曼哈顿距离,不然真的是得有KD-Tree还真不会),我们在输入的时候把x存入x_hash[]的数组中去,存完后对x_hash[]进行个升序排序,然后就可以用unique()函数进行去重操作,它的去重并非真正意义上的删除,只是把相同的元素排到最后面去而已,然后利用数组的特性去得道不重复元素的个数,然后题目讲到一点,可能有多个雷在同一点上,于是我一开始想写的set()改成了multiset()——不去重的set(),之后,清空其1~hash_cnt(哈希表的长度,不同元素的个数)的元素,留空出来好插入元素,之后,对全体元素进行逐一处理,现在的x的正确ID变成了它在哈希表上的位置了。

  以上是离散化的操作,然后我们来探讨一下——如何去知道到底这次的爆炸会炸开多少地雷?已知点->未知点BFS试一下,还真的可行,我们知道起点s,就是自爆点,其中要是自爆点已经爆过了,那么直接返回0,然后就是正规操作了,对于起点的x,我们可以用其知道x在哈希表上的可行范围(用二分的lower_bound()或者upper_bound()来做),x的哈希表上的范围在于目前点的坐标的圆最大可达距离点,然后我们再由x的遍历去限制y(也可以说是如此去找到对应的y),也是需要二分来查找的,这时候就意味着需要重载结构体中的函数了,将x看作数组的下标,以此查找区间内满足条件的(x,y)点,对于y,我们直接查找它在结构体的哪个位置,同样的查找的区间为满足目前点的圆的最大可达距离,但是因为此时的半径不再是其原本半径了,而是要剪去对应已经走出的x的距离才是y的半径,然后直接用查询到的点所“->”的ID是否爆炸,最后退出循环的时候最好删除这些已经遍历过的点,这样可以节约时间。

 

完整代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
typedef long long ll;
const int maxN=100005;
int N, M;
int Abs(int x) { return x>0?x:(-x); }
bool cmp(int e1, int e2) { return e1<e2; }
bool boom[maxN];    //是否已经爆炸的点
struct point        //记录正统(原)状态
{
    int x, y, rd;
    point(int a=0, int b=0, int c=0):x(a), y(b), rd(c) {}
}a[maxN];
int x_hash[maxN], hash_cnt;     //对于x进行哈希(离散化),得出一共有多少个哈希点
struct node     //是哈希过后,用其记录对应的y坐标,以及该数的id,毕竟关系到爆炸点问题
{
    int y, id;      //y坐标,与x的对应点
    node(int a=0, int b=0):y(a), id(b) {}
    friend bool operator < (node e1, node e2)
    {
        return e1.y<e2.y;
    }
};
multiset<node> mst[maxN];
int bfs(int s)
{
    if(boom[s]) return 0;
    int ans=0;  boom[s]=true;
    queue<int> Q;
    Q.push(s);
    multiset<node>::iterator yl, yr, it;
    while(!Q.empty())
    {
        ans++;
        int u=Q.front();    Q.pop();
        int xl=(int)( lower_bound(x_hash+1, x_hash+1+hash_cnt, a[u].x-a[u].rd) - x_hash );  //下限
        int xr=(int)( upper_bound(x_hash+1, x_hash+1+hash_cnt, a[u].x+a[u].rd) - x_hash - 1);  //上限
        for(int i=xl; i<=xr; i++)
        {
            int yy=a[u].rd - Abs(a[u].x - x_hash[i]);
            yl=mst[i].lower_bound(node(a[u].y - yy, 1));
            yr=mst[i].upper_bound(node(a[u].y + yy, 1));
            for(it=yl; it!=yr; it++)
            {
                if(!boom[it->id])
                {
                    boom[it->id]=true;
                    Q.push(it->id);
                }
            }
            mst[i].erase(yl, yr);
        }
    }
    return ans;
}
int main()
{
    int Cas=0;
    while(scanf("%d", &N) && N)
    {
        for(int i=1; i<=N; i++)
        {
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].rd);
            x_hash[i]=a[i].x;
        }
        sort(x_hash+1, x_hash+1+N, cmp);
        hash_cnt=(int)( unique(x_hash+1, x_hash+1+N) - x_hash - 1);  //unique并非把相同的去掉了,而是把他们放到了最后面
        for(int i=1; i<=hash_cnt; i++) { mst[i].clear(); }
        for(int i=1; i<=N; i++)
        {
            int id=(int)( lower_bound(x_hash+1, x_hash+1+hash_cnt, a[i].x) - x_hash );
            mst[id].insert(node(a[i].y, i));        //以x所在的序位来推y
        }
        scanf("%d", &M);        //M次查询操作
        printf("Case #%d:\n", ++Cas);
        memset(boom, false, sizeof(boom));
        for(int i=1; i<=M; i++)
        {
            int e1;     scanf("%d", &e1);
            printf("%d\n", bfs(e1));
        }
    }
    return 0;
}

 

第二次写

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
typedef long long ll;
const int maxN=100005;
int N, M;
bool cmp(int e1, int e2) { return e1<e2; }
int Abs(int x) { return x>0?x:(-x); }
struct mine
{
    int x, y, r;
    mine(int a=0, int b=0, int c=0):x(a), y(b), r(c) {}
}point[maxN];
int x_hash[maxN], hash_cnt; bool boom[maxN];
struct node
{
    int y, id;
    node(int a=0, int b=0):y(a),id(b) {}
    friend bool operator < (node e1, node e2)
    {
        return e1.y<e2.y;
    }
};
multiset<node> mst[maxN];
int bfs(int s)
{
    if(boom[s]) return 0;
    int ans=0;
    queue<int> Q;
    Q.push(s); boom[s]=true;
    while(!Q.empty())
    {
        int u=Q.front();    Q.pop();    ans++;
        int xl, xr; multiset<node>::iterator yl, yr, it;
        xl=(int)( lower_bound(x_hash+1, x_hash+1+hash_cnt, point[u].x - point[u].r) - x_hash);
        xr=(int)( upper_bound(x_hash+1, x_hash+1+hash_cnt, point[u].x + point[u].r) - x_hash - 1);
        for(int i=xl; i<=xr; i++)
        {
            int yy=point[u].r - Abs(point[u].x - x_hash[i]);
            yl=mst[i].lower_bound(node(point[u].y-yy, 1));
            yr=mst[i].upper_bound(node(point[u].y+yy, 1));
            for(it=yl; it!=yr; it++)
            {
                if(!boom[it->id])
                {
                    Q.push(it->id);
                    boom[it->id]=true;
                }
            }
            mst[i].erase(yl, yr);
        }
    }
    return ans;
}
int main()
{
    int Cas=0;
    while(scanf("%d", &N) && N)
    {
        for(int i=1; i<=N; i++)
        {
            scanf("%d%d%d", &point[i].x, &point[i].y, &point[i].r);
            x_hash[i]=point[i].x;
        }
        sort(x_hash+1, x_hash+1+N, cmp);
        hash_cnt=(int)( unique(x_hash+1, x_hash+1+N) - x_hash - 1);
        for(int i=1; i<=hash_cnt; i++) mst[i].clear();
        for(int i=1; i<=N; i++)
        {
            int id=(int)( lower_bound(x_hash+1, x_hash+1+hash_cnt, point[i].x) - x_hash );
            mst[id].insert(node(point[i].y, i));
        }
        scanf("%d", &M);
        printf("Case #%d:\n", ++Cas);   memset(boom, false, sizeof(boom));
        for(int i=1; i<=M; i++)
        {
            int id; scanf("%d", &id);
            printf("%d\n", bfs(id));
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值