首页 > 代码库 > Building in Sandbox

Building in Sandbox

本题来自hihocoder#1291
 
时间限制:30000ms
单点时限:3000ms
内存限制:256MB

描述

Little Hi is playing a sandbox voxel game. In the game the whole world is constructed by massive 1x1x1 cubes. The edges of cubes are parallel to the coordinate axes and the coordinates (x, y, z) of the center of each cube are integers.

技术分享

At the beginning there is nothing but plane ground in the world. The ground consists of all the cubes of z=0. Little Hi needs to build everything by placing cubes one by one following the rules:

1. The newly placed cube must be adjacent to the ground or a previously placed cube. Two cubes are adjacent if and only if they share a same face.

2. The newly placed cube must be accessible from outside which means by moving in 6 directions(up, down, left, right, forward, backward) there is a path from a very far place - say (1000, 1000, 1000) in this problem - to this cube without passing through ground or other cubes.

Given a sequence of cubes Little Hi wants to know if he can build the world by placing the cubes in such order.

输入

The first line contains the number of test cases T(1 <= T <= 10).

For each test case the first line is N the number of cubes in the sequence.

The following N lines each contain three integers x, y and z indicating the coordinates of a cube.

 

For 20% of the data, 1 <= N <= 1000, 1 <= x, y, z <= 10.

For 100% of the data, 1 <= N <= 100000, 1 <= x, y, z <= 100.

输出

For each testcase output "Yes" or "No" indicating if Little Hi can place the cubes in such order.

样例提示

In the first test case three cubes are placed on the ground. It‘s OK.

In the second test case (1, 3, 2) is neither on the ground nor adjacent to previous cubes. So it can‘t be placed.

In the last test case (2, 2, 1) can not be reached from outside. So it can‘t be placed.  

样例输入

3
3
1 1 1
1 2 1
1 3 1
3
1 1 1
1 2 1
1 3 2
17
1 1 1
1 2 1
1 3 1
2 3 1
3 3 1
3 2 1
3 1 1
2 1 1
2 1 2
1 1 2
1 2 2
1 3 2
2 3 2
3 3 2
3 2 2
2 2 2
2 2 1

样例输出

Yes
No
No

对于这道题,我们需要检测一个方块序列能不能被逐个放置,只需检查两个条件,第一个条件是检查新放入的方块是否与之前放入的方块相邻,这个很容易做到。关键在于第二个条件,我们需要检测新放入的方块能否通往无穷远处。一个基本的想法是使用floodfill,不过如果直接对每个方块使用这个方法的话,我们的算法会超时。一个想法是,我们希望能够动态地维护哪些地方可以通往无穷远处,但是问题在于,我们很难判断一个放入方块的操作是否会导致某一块区域被封闭起来。于是这道题的突破口正在此处,如果我们按顺序往里放方块的话,我们很难判断放入方块是否会封闭住一些区域,但是如果我们考虑一个逆向的过程,往外拿方块呢?每拿一个方块,我们就在拿掉的地方进行一次floodfill,这样我们就能算出拿掉这个方块后我们增加了哪些外部点。同时,我们只需要查看一个方块是否与外部点相邻,就能判断能否拿掉这个方块。容易验证,我们能顺序地放入方块,当且仅当满足相邻条件,且我们可以逆向的拿掉方块。

代码如下:

#include<iostream>
#include<vector>
#include<deque>
#include<stack>
#include<string>
#include<algorithm>
#include<string.h>
#include<sstream>

struct Point {
    int x, y, z;
    Point() : x(0), y(0), z(0) {};
    Point(int a, int b, int c) : x(a), y(b), z(c) {}
};

int MAX_C;
const int MAX_N = 100000+5;
const int dir[6][3] = {
    {1, 0, 0},
    {0, 1, 0},
    {0, 0, 1},
    {-1, 0, 0},
    {0, -1, 0},
    {0, 0, -1}
};


int pos[100 + 2][100 + 2][100 + 2];
int is_out[100 + 2][100 + 2][100 + 2];
Point cors[MAX_N];

using namespace std;


bool has_adj(int x, int y, int z) {
    if (z == 1) return true;

    for (int d = 0; d < 6; ++d) {
        int x1 = x + dir[d][0];
        int y1 = y + dir[d][1];
        int z1 = z + dir[d][2];

        if (pos[x1][y1][z1]) return true;
    }

    return false;
}

bool can_take(int x, int y, int z) {
    for (int d = 0; d < 6; ++d) {
        int x1 = x + dir[d][0];
        int y1 = y + dir[d][1];
        int z1 = z + dir[d][2];

        if (is_out[x1][y1][z1]) return true;
    }

    return false;
}

void my_bfs(int x, int y, int z) {
    deque<Point> q;
    Point p0(x, y, z);
    q.push_back(p0);

    while (!q.empty()) {
        Point p = q[0];
        q.pop_front();

        if (p.z < 1 || p.z > MAX_C + 1 || p.x < 0 || p.x > MAX_C + 1 || p.y < 0 || p.y > MAX_C + 1) continue;
        if (is_out[p.x][p.y][p.z] || pos[p.x][p.y][p.z]) continue;
        

        is_out[p.x][p.y][p.z] = 1;
        for (int d = 0; d < 6; ++d) {
            int x1 = p.x + dir[d][0];
            int y1 = p.y + dir[d][1];
            int z1 = p.z + dir[d][2];

            Point p1(x1, y1, z1);
            q.push_back(p1);
        }
    
    }

}

int main() {
    int T;
    cin >> T;
    for (int t = 0; t < T; ++t) {
        int N;
        cin >> N;
        memset(pos, 0, sizeof(pos));
        memset(is_out, 0, sizeof(is_out));

        bool is_possible = true;
        MAX_C = 0;
        for (int i = 0; i < N; ++i) {
            int x, y, z;
            cin >> x >> y >> z;
            cors[i].x = x;
            cors[i].y = y;
            cors[i].z = z;
            MAX_C = max(x, MAX_C);
            MAX_C = max(y, MAX_C);
            MAX_C = max(z, MAX_C);
            if (!pos[x][y][z] && has_adj(x, y, z)) {
                pos[x][y][z] = 1;
            }
            else {
                is_possible = false;
                //break;
            }
        }

        if (!is_possible) {
            cout << "No" << endl;
            continue;
        }

        else {
            my_bfs(0, 0, 1);

            for (int i = N - 1; i >= 0; --i) {
                int x = cors[i].x;
                int y = cors[i].y;
                int z = cors[i].z;
                if (!can_take(x, y, z)) {
                    is_possible = false;
                    break;
                }
                pos[x][y][z] = 0;
                my_bfs(x, y, z);
            }

            if (is_possible) cout << "Yes" << endl;
            else cout << "No" << endl;
        
        }
    }

    //system("pause");
    return 0;
}

 

Building in Sandbox