题意
给定n颗炸弹,每颗炸弹有一个左边x,y以及一个爆炸时间,一个炸弹爆炸会牵引不超过k距离的其它炸弹爆炸(如下图k=2时),每秒可以点燃一个炸弹,问最少多少秒所有炸弹爆炸。
题解
假如没有牵连爆炸这个限制,这道题就变成一个简单的贪心问题,排序每次选择爆炸时间最长的一个点燃,当时间已经比下一个时间长时就是答案,那么有了牵连爆炸的限制后该怎么做呢?其实我们可以把会一起爆炸的炸弹看成一个炸弹,也就可以用并查集来维护,然后它们爆炸的时间就是集合中爆炸时间最短的那个。再之后就变成那个简单的贪心问题了。
#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define fr first
#define sc second
#define ll long long
const long long inf = 1e18;
long long n, d, nn, a[200069], dsu[200069], ex[200069];
pair<pair<long long, long long>, long long> as[200069];//存储每颗雷的信息
long long fd(long long x)//并查集
{
if (dsu[x] != x)
{
dsu[x] = fd(dsu[x]);
}
return dsu[x];
}
int main()
{
long long t, rr, i, ii, l, y, x, p, z;
scanf("%lld", &t);//组数
for (rr = 0; rr < t; rr++)
{
scanf("%lld%lld", &n, &d);//n颗雷 半径为d的地雷会爆炸
for (i = 1; i <= n; i++)
{
scanf("%lld%lld%lld", &x, &y, &a[i]);//a存储下班为i的爆炸时间为i
as[i] = { {x,y},i };//雷的信息,y,x,第i颗雷
dsu[i] = i;//并查集初始化
}
for (ii = 0; ii < 2; ii++)//两次,一次跑x一次跑y
{
sort(as + 1, as + n + 1);//根据第一坐标优先其次第二坐标优先在者编号优先从小到大排序
for (i = 1; i <= n; i++)
{
y = as[i].fr.fr;//x/y
x = as[i].fr.sc;//y/x
p = as[i].sc;//当前 编号
if (i > 1 && y == as[i - 1].fr.fr && x - as[i - 1].fr.sc <= d)//一个坐标相同,另外一个坐标相差为d就为一个集合的雷
{
l = as[i - 1].sc;
a[fd(p)] = min(a[fd(p)], a[fd(l)]);//爆炸时间为较小的那个
dsu[fd(l)] = fd(p);//合并成一个集合p
}
}
for (i = 1; i <= n; i++)//交换x,y坐标再跑一次
{
swap(as[i].fr.fr, as[i].fr.sc);
}
}
//接下来的问题就变成了有nn个爆炸时间不同的雷,每秒可以点爆一个,最快几秒可以点爆完成,贪心的去做,每次都点爆离当前时间最小的一颗雷即可
nn = 0;
for (i = 1; i <= n; i++)
{
if (fd(i) == i)
{
nn++;
ex[nn] = a[i];
}
}
sort(ex + 1, ex + nn + 1, greater<long long>());
ex[nn + 1] = -inf;
for (i = 1; i <= nn; i++)
{
if (ex[i + 1] <= i - 1)break;
}
printf("%lld\n", i-1);
}
}