玩命加载中 . . .

Unusual Minesweeper


问题传送门
https://codeforces.com/problemset/problem/1619/G

题目大意

另类的“扫雷”游戏,给出若干个地雷的信息(横坐标、纵坐标、自动爆炸时间),另外规定,每一分钟,可以人为地指定任意一颗地雷爆炸,并且地雷爆炸会引发同行、同列所有距离k以内的地雷爆炸,从而产生连锁反应。试问引爆地图上所有地雷所需要的最少时间是多少。

题目分析

首先我们知道的是,每一颗地雷爆炸的形式有三种:

  • 到达一定时间自动爆炸
  • 被其他的地雷引爆
  • 人为引爆

如果单纯一个一个考虑三个条件,将会使本题变得十分复杂。那么,我们可不可以将三个条件作出一定的简化,或者合并一下,从而使得问题处理起来更加简便而清晰呢?

当然是可以的。

  1. 根据题意,地雷的连锁引爆无疑是本题的关键之处;这一部分的处理,需要用到深度优先搜索DFS;
  2. 任何一颗地雷的爆炸都不是单纯它一个的爆炸,还需要考虑它引发的所有地雷的爆炸;这样一来,这些被同一颗地雷引爆的所有地雷的爆炸时间全部都是一样的,可以将它们看作一个整体。
  3. 那么怎么找到这样的整体呢?事实上,这样的地雷群体,只要其中任意一个爆炸,整个群体都会爆炸,也就是说,对任何一颗地雷进行DFS,都可以牵扯出整个地雷群;
  4. 还需要另设一个标记,记录哪些地雷已经爆炸过。

具体实现

由于地雷坐标实在太大,不方便直接使用二维数组存储相关信息,我们可以换一种方式存储:使用map容器,存储每一横行和每一纵列的地雷的坐标,这样也方便后续的DFS处理。
另外,设置容器记录每一颗地雷的爆炸时间以及是否爆炸;

// 存储指定坐标下,一整行(列)的地雷坐标
map<ll, vector<ll>>mapX;
map<ll, vector<ll>>mapY;
map<pair<ll, ll>, bool>isBoo;
map<pair<ll, ll>, ll>times;
...
while (i<n)
{
   scanf("%lld%lld%lld", &x, &y, &t);
   alls[i++]=make_pair(x, y);
   mapX[x].push_back(y);
   mapY[y].push_back(x);
   times[{x, y}] = t;
}
...

对所有地雷遍历,也就是上面说过的,任意一个地雷爆炸可以牵扯出整个地雷群,只需要求出这个地雷群的最短爆炸时间即可,用到DFS:

ll dfs(ll x, ll y)
{
    // 让{x,y}处地雷爆炸
    ll minT = times[{x, y}];
    isBoo[{x, y}] = true;
    // 连锁反应爆炸开始
    auto i = lower_bound(mapX[x].begin(), mapX[x].end(), y);
    auto j = lower_bound(mapY[y].begin(), mapY[y].end(), x);

    // 四个方向
    if (++i != mapX[x].end() && !isBoo[{x, * i}] && abs(*i - y) <= k)
    {
        ll tm = dfs(x, *i);
        minT = minT > tm ? tm : minT;
    }
    i--;
    if (i != mapX[x].begin() && !isBoo[{x, * (--i)}] && abs(*i - y) <= k)
    {
        ll tm = dfs(x, *i);
        minT = minT > tm ? tm : minT;
    }
    if (++j != mapY[y].end() && !isBoo[{*j, y}] && abs(*j - x) <= k)
    {
        ll tm = dfs(*j, y);
        minT = minT > tm ? tm : minT;
    }
    j--;
    if (j != mapY[y].begin() && !isBoo[{*(--j), y}] && abs(*j - x) <= k)
    {
        ll tm = dfs(*j, y);
        minT = minT > tm ? tm : minT;
    }
    return minT;
}

这样一来,所有的地雷所需爆炸时间就会被分开,在数组中的每一个值都代表一个不同的地雷群的爆炸时间,也就是说,它们互不影响(地雷群之间不会因为爆炸而相互引爆了,否则两个地雷群会合成一个)。采用双指针对这个时间数组进行遍历,以时间为循环变量,直到双指针相遇即可。

vector<ll> pres;// 存储dfs的结果,得到的就是所有地雷群的爆炸时间
for (int i=0;i<alls.size()-1;i++)
{
   auto e = alls[i];
   if(!isBoo[e])
       pres.push_back(dfs(e.first, e.second));
}

// 需要对pres进行升序排序,最后面的地雷当然需要每一次的人为引爆
sort(pres.begin(), pres.end());
// 设置头指针sp,尾指针ep,ans就是最后的答案————爆炸完的最短时间
ll ep = pres.size() - 1, sp = 0, ans = 0;
for (;; ans++)
{
   while (pres[sp] == ans)   sp++;
   if (ep <= sp)  break;
   ep--;
}
cout << ans << endl;

完整代码

// unusualmineSweeper.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
typedef long long ll;
using namespace std;
// 存储指定坐标下,一整行(列)的地雷坐标
map<ll, vector<ll>>mapX;
map<ll, vector<ll>>mapY;

map<pair<ll, ll>, bool>isBoo;
map<pair<ll, ll>, ll>times;
ll x, y, k, t;
int q, n;

ll dfs(ll x, ll y)
{
    // 让{x,y}处地雷爆炸
    ll minT = times[{x, y}];
    isBoo[{x, y}] = true;
    // 连锁反应爆炸开始
    auto i = lower_bound(mapX[x].begin(), mapX[x].end(), y);
    auto j = lower_bound(mapY[y].begin(), mapY[y].end(), x);

    // 四个方向
    if (++i != mapX[x].end() && !isBoo[{x, * i}] && abs(*i - y) <= k)
    {
        ll tm = dfs(x, *i);
        minT = minT > tm ? tm : minT;
    }
    i--;
    if (i != mapX[x].begin() && !isBoo[{x, * (--i)}] && abs(*i - y) <= k)
    {
        ll tm = dfs(x, *i);
        minT = minT > tm ? tm : minT;
    }
    if (++j != mapY[y].end() && !isBoo[{*j, y}] && abs(*j - x) <= k)
    {
        ll tm = dfs(*j, y);
        minT = minT > tm ? tm : minT;
    }
    j--;
    if (j != mapY[y].begin() && !isBoo[{*(--j), y}] && abs(*j - x) <= k)
    {
        ll tm = dfs(*j, y);
        minT = minT > tm ? tm : minT;
    }
    return minT;
}

int main()
{
    scanf("%d", &q);
    while (q--)
    {
        mapX.clear();
        mapY.clear();
        isBoo.clear();
        scanf("%d%lld", &n, &k);
        vector<pair<ll, ll>> alls(n+1);  // 存放全部地雷的坐标对
        int i = 0;
        while (i<n)
        {
            scanf("%lld%lld%lld", &x, &y, &t);
            alls[i++]=make_pair(x, y);
            mapX[x].push_back(y);
            mapY[y].push_back(x);
            times[{x, y}] = t;
        }
        for (auto item : mapX)
        {
            sort(mapX[item.first].begin(), mapX[item.first].end());
        }
        for (auto item : mapY)
        {
            sort(mapY[item.first].begin(), mapY[item.first].end());
        }

        vector<ll> pres;
        for (int i=0;i<alls.size()-1;i++)
        {
            auto e = alls[i];
            if(!isBoo[e])
                pres.push_back(dfs(e.first, e.second));
        }

        sort(pres.begin(), pres.end());
        ll ep = pres.size() - 1, sp = 0, ans = 0;
        for (;; ans++)
        {
            while (pres[sp] == ans)   sp++;
            if (ep <= sp)  break;
            ep--;
        }
        cout << ans << endl;
    }
}

文章作者: 鹿卿
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 鹿卿 !
评论
  目录