Submission #32136330


Source Code Expand

#include <bits/stdc++.h>
using pii=std::pair<int,int>;
using namespace std;

const int maxnsq = 105, GRID_CHOOSE_TIMEOUT_MILLIS = 2500;

enum direction { left_bit, up_bit, right_bit, down_bit };
vector<int> direction_iterable = {left_bit, up_bit, right_bit, down_bit};
pii dirs[4] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
map<pii, char> movement_to_direction_character_map = {
    {{0, -1}, 'L'}, 
    {{-1, 0}, 'U'}, 
    {{0, 1}, 'R'}, 
    {{1, 0}, 'D'}
};

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    
int n, t;

int is_valid(int x, int y)
{
    return x >= 0 && x < n && y >= 0 && y < n;
}

int is_valid_for_grid_generator(int x, int y)
{
    return is_valid(x, y) && !(x == 0 && y == 0);
}

int cnt_adjacent_connecting(int mask, int x, int y, vector<vector<int>>& grid_masks)
{
    int adj_cnt = 0;
    for(auto dir_bit : direction_iterable)
        if(mask & (1ll << dir_bit))
        {
            int nex = x + dirs[dir_bit].first;
            int ney = y + dirs[dir_bit].second;
            if(!is_valid_for_grid_generator(nex, ney))
                continue;
            int connecting_dir_bit = (dir_bit + 2) % 4;
            if(grid_masks[nex][ney] & (1ll << connecting_dir_bit))
                adj_cnt++;
        }
    return adj_cnt;
}

int has_adjacent_connecting(int x, int y, vector<vector<int>>& grid_masks)
{
    return cnt_adjacent_connecting(15, x, y, grid_masks) > 0;
}

int is_good_placement(int mask, int curx, int cury, int fromx, int fromy, vector<vector<int>>& grid_masks)
{
    int from_valid = (fromx == -1 && fromy == -1);
    for(auto dir_bit : direction_iterable)
        if(mask & (1ll << dir_bit))
        {
            int nex = curx + dirs[dir_bit].first;
            int ney = cury + dirs[dir_bit].second;
            if(nex == fromx && ney == fromy)
            {
                from_valid = 1;
                continue;
            }
            if(!is_valid_for_grid_generator(nex, ney) || grid_masks[nex][ney] != 0 || has_adjacent_connecting(nex, ney, grid_masks))
                return 0;
        }
    return from_valid;
}


pair<vector<vector<int>>, int> get_random_tree(vector<int> mask_cnts)
{
    vector<vector<int>> grid_masks(n, vector<int>(n, 0));
    int comp_sz = 0;

    vector<vector<int>> visited(n, vector<int>(n));
    vector<int> mask_order(15);
    iota(mask_order.begin(), mask_order.end(), 1);
    shuffle(mask_order.begin(), mask_order.end(), rng);
    sort(mask_order.begin(), mask_order.end(), [](const int& m1, const int& m2) {
        return __builtin_popcount(m1) > __builtin_popcount(m2);
    });

    int startx = uniform_int_distribution<int>(0, n - 1)(rng);
    int starty = uniform_int_distribution<int>(0, n - 1)(rng);
    while(startx == 0 && starty == 0)
    {
        startx = uniform_int_distribution<int>(0, n - 1)(rng);
        starty = uniform_int_distribution<int>(0, n - 1)(rng);
    }
    stack<pair<pii, pii>> s; // {{curx, cury}, {fromx, fromy}}
    s.push({{startx, starty}, {-1, -1}});
    visited[startx][starty] = 1;
    while(!s.empty())
    {
        int curx, cury, fromx, fromy;
        tie(curx, cury) = s.top().first;
        tie(fromx, fromy) = s.top().second;
        s.pop();
        for(auto mask : mask_order)
            if(mask_cnts[mask] > 0 && is_good_placement(mask, curx, cury, fromx, fromy, grid_masks))
            {
                grid_masks[curx][cury] = mask;
                mask_cnts[mask]--;
                for(auto dir_bit : direction_iterable)
                    if(mask & (1ll << dir_bit))
                    {
                        int nex = curx + dirs[dir_bit].first;
                        int ney = cury + dirs[dir_bit].second;
                        if(nex == fromx && ney == fromy)
                            continue;
                        assert(!visited[nex][ney]);
                        visited[nex][ney] = 1;
                        s.push({{nex, ney}, {curx, cury}});
                    }
                comp_sz++;
                break;
            }
    }
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            if(!(i == 0 && j == 0) && grid_masks[i][j] == 0)
                for(int mask = 1; mask <= 15; mask++)
                    if(mask_cnts[mask] > 0 && cnt_adjacent_connecting(mask, i, j, grid_masks) == 1)
                    {
                        grid_masks[i][j] = mask;
                        mask_cnts[mask]--;
                        comp_sz++;
                        break;
                    }
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            if(!(i == 0 && j == 0) && grid_masks[i][j] == 0)
                for(int mask = 1; mask <= 15; mask++)
                    if(mask_cnts[mask] > 0)
                    {
                        grid_masks[i][j] = mask;
                        mask_cnts[mask]--;
                        break;
                    }
    return {grid_masks, comp_sz};
}

void calc_shortest_path(int fromx, int fromy, vector<vector<int>>& dist, vector<vector<pii>>& from, vector<vector<int>>& fixed)
{
    assert(is_valid(fromx, fromy) && !fixed[fromx][fromy]);
    queue<pii> q;
    dist[fromx][fromy] = 0;
    q.push({fromx, fromy});
    while(!q.empty())
    {
        int curx, cury;
        tie(curx, cury) = q.front();
        q.pop();
        for(auto ne : dirs)
        {
            int nex = curx + ne.first;
            int ney = cury + ne.second;
            if(is_valid(nex, ney) && !fixed[nex][ney] && dist[curx][cury] + 1 < dist[nex][ney])
            {
                dist[nex][ney] = dist[curx][cury] + 1;
                from[nex][ney] = {curx, cury};
                q.push({nex, ney});
            }
        }
    }
}

vector<pii> recover_path(int tox, int toy, vector<vector<pii>>& from)
{
    vector<pii> path;
    int atx = tox, aty = toy;
    do
    {
        path.push_back({atx, aty});
        tie(atx, aty) = from[atx][aty];
    } while(make_pair(atx, aty) != make_pair(-1, -1));
    reverse(path.begin(), path.end());
    return path;
}

vector<pii> get_shortest_path(int fromx, int fromy, int tox, int toy, vector<vector<int>>& fixed)
{
    assert(is_valid(fromx, fromy) && is_valid(tox, toy) && !fixed[fromx][fromy] && !fixed[tox][toy]);
    vector<vector<int>> dist(n, vector<int>(n, 2e9));
    vector<vector<pii>> from(n, vector<pii>(n, {-1, -1}));
    
    calc_shortest_path(fromx, fromy, dist, from, fixed);
    return recover_path(tox, toy, from);
}

vector<pii> get_path_from_closest_occurance(int tox, int toy, int target_value, vector<vector<int>>& current_grid, vector<vector<int>>& fixed)
{
    assert(is_valid(tox, toy) && !fixed[tox][toy]);
    vector<vector<int>> dist(n, vector<int>(n, 2e9));
    vector<vector<pii>> from(n, vector<pii>(n, {-1, -1}));
    
    calc_shortest_path(tox, toy, dist, from, fixed);
    int fromx = -1, fromy = -1;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            if(current_grid[i][j] == target_value && ((fromx == -1 && fromy == -1) || dist[i][j] < dist[fromx][fromy]))
            {
                fromx = i;
                fromy = j;
            }
    assert(fromx != -1 && fromy != -1);
    vector<pii> result = recover_path(fromx, fromy, from);
    reverse(result.begin(), result.end());
    return result;
}

string apply_and_get_operation_string(vector<pii> path, vector<vector<int>>& current_grid)
{
    assert(!path.empty());
    int current_start_val = current_grid[path[0].first][path[0].second];
    string ops;
    for(int i = 1; i < path.size(); i++)
    {
        int movex = path[i].first - path[i - 1].first;
        int movey = path[i].second - path[i - 1].second;
        assert(movement_to_direction_character_map.count({movex, movey}));
        ops += movement_to_direction_character_map[{movex, movey}];
        current_grid[path[i - 1].first][path[i - 1].second] = current_grid[path[i].first][path[i].second];
    }
    current_grid[path.back().first][path.back().second] = current_start_val;
    return ops;
}

string move_cell(vector<pii> path, vector<vector<int>>& current_grid, vector<vector<int>>& fixed, int& empty_cell_x, int& empty_cell_y)
{
    // vector<pii> path = get_path_from_closest_occurance(tox, toy, target_value, current_grid, fixed);
    int tox = path.back().first, toy = path.back().second;
    int target_value = current_grid[path[0].first][path[0].second];
    assert(!path.empty());
    string ops;
    for(int i = 1; i < path.size(); i++)
    {
        // Protect the value we intend to move (currently at path[i - 1]), so we don't accidentally change its position while moving the empty cell.
        fixed[path[i - 1].first][path[i - 1].second] = 1;
        // Move empty cell to target cell (the adjacent cell we intended to move the value to)
        ops += apply_and_get_operation_string(get_shortest_path(empty_cell_x, empty_cell_y, path[i].first, path[i].second, fixed), current_grid);
        fixed[path[i - 1].first][path[i - 1].second] = 0;
        ops += apply_and_get_operation_string({path[i], path[i - 1]}, current_grid);
        tie(empty_cell_x, empty_cell_y) = path[i - 1];
    }
    assert(current_grid[tox][toy] == target_value);
    return ops;
}


string exact_slide_solver(vector<vector<int>>& current, vector<vector<int>>& target)
{
    int empty_cell_x = -1, empty_cell_y = -1;
    vector<int> mask_cnts(16);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        {
            mask_cnts[current[i][j]]++;
            if(current[i][j] == 0)
            {
                assert(empty_cell_x == -1 && empty_cell_y == -1);
                empty_cell_x = i;
                empty_cell_y = j;
            }
        }
    assert(empty_cell_x != -1 && empty_cell_y != -1);

    vector<vector<int>> fixed(n, vector<int>(n, 0));

    
    string ans;
    for(int i = n - 1; i > 2; i--)
    {
        // Fix last row
        for(int j = i; j > 1; j--)
        {
            ans += move_cell(get_path_from_closest_occurance(i, j, target[i][j], current, fixed), current, fixed, empty_cell_x, empty_cell_y);
            fixed[i][j] = 1;
        }
        // Handle first two columns of last row
        // Written this way to prevent issues with the first getting stuck, should be optimizable.
        ans += move_cell(get_path_from_closest_occurance(0, 0, target[i][0], current, fixed), current, fixed, empty_cell_x, empty_cell_y);
        fixed[0][0] = 1;
        ans += move_cell(get_path_from_closest_occurance(i, 0, target[i][1], current, fixed), current, fixed, empty_cell_x, empty_cell_y);
        fixed[0][0] = 0;
        fixed[i][0] = 1;
        ans += move_cell(get_shortest_path(0, 0, i - 1, 0, fixed), current, fixed, empty_cell_x, empty_cell_y);
        fixed[i - 1][0] = 1;
        ans += apply_and_get_operation_string(get_shortest_path(empty_cell_x, empty_cell_y, i, 1, fixed), current);
        fixed[i][0] = fixed[i - 1][0] = 0;
        empty_cell_x = i - 1;
        empty_cell_y = 0;
        ans += apply_and_get_operation_string({make_pair(i, 1), make_pair(i, 0), make_pair(i - 1, 0)}, current);
        fixed[i][0] = fixed[i][1] = 1;
        // Fix last col
        for(int j = i - 1; j > 1; j--)
        {
            ans += move_cell(get_path_from_closest_occurance(j, i, target[j][i], current, fixed), current, fixed, empty_cell_x, empty_cell_y);
            fixed[j][i] = 1;
        }
        // Handle first two rows of last column
        // Written this way to prevent issues with the first getting stuck, should be optimizable.
        ans += move_cell(get_path_from_closest_occurance(0, 0, target[0][i], current, fixed), current, fixed, empty_cell_x, empty_cell_y);
        fixed[0][0] = 1;
        ans += move_cell(get_path_from_closest_occurance(0, i, target[1][i], current, fixed), current, fixed, empty_cell_x, empty_cell_y);
        fixed[0][0] = 0;
        fixed[0][i] = 1;
        ans += move_cell(get_shortest_path(0, 0, 0, i - 1, fixed), current, fixed, empty_cell_x, empty_cell_y);
        fixed[0][i - 1] = 1;
        ans += apply_and_get_operation_string(get_shortest_path(empty_cell_x, empty_cell_y, 1, i, fixed), current);
        fixed[0][i] = fixed[0][i - 1] = 0;
        empty_cell_x = 0;
        empty_cell_y = i - 1;
        ans += apply_and_get_operation_string({make_pair(1, i), make_pair(0, i), make_pair(0, i - 1)}, current);
        fixed[0][i] = fixed[1][i] = 1;
    }
    // TODO: Figure out how to solve if the 3 cells of the 2x2 are rotationally opposite of what we need.
    return ans;
}

int32_t main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> t;
    vector<int> mask_cnts(16);
    vector<vector<int>> grid_masks(n, vector<int>(n));
    for(int i = 0; i < n; i++)
    {
        string s;
        cin >> s;
        for(int j = 0; j < n; j++)
        {
            grid_masks[i][j] = (s[j] >= 'a' && s[j] <= 'z') ? (s[j] - 'a' + 10) : (s[j] - '0');
            mask_cnts[grid_masks[i][j]]++;
        }
    }
    vector<vector<int>> best_grid;
    int best_score = 0;
    auto grid_choose_start = chrono::high_resolution_clock::now();
    int times = 0;
    while(chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - grid_choose_start).count() < GRID_CHOOSE_TIMEOUT_MILLIS)
    {
        vector<vector<int>> cur_grid;
        int cur_score;
        tie(cur_grid, cur_score) = get_random_tree(mask_cnts);
        if(cur_score > best_score)
        {
            best_score = cur_score;
            best_grid = cur_grid;
        }
        times++;
    }
    assert(best_grid[0][0] == 0);
    for(auto row : best_grid)
        for(auto x : row)
            mask_cnts[x]--;
    for(int i = 0; i <= 15; i++)
        assert(mask_cnts[i] == 0);
    string ans = exact_slide_solver(grid_masks, best_grid);
    while(ans.size() > t)
        ans.pop_back();
    assert(ans.size() <= t);
    cout << ans << "\n";
    return 0;
}

Submission Info

Submission Time
Task A - Sliding Tree Puzzle
User ExplodingFreeze
Language C++ (GCC 9.2.1)
Score 19988760
Code Size 14262 Byte
Status AC
Exec Time 2503 ms
Memory 3692 KiB

Compile Error

./Main.cpp: In function ‘std::string apply_and_get_operation_string(std::vector<std::pair<int, int> >, std::vector<std::vector<int> >&)’:
./Main.cpp:220:22: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<std::pair<int, int> >::size_type’ {aka ‘long unsigned int’} [-Wsign-compare]
  220 |     for(int i = 1; i < path.size(); i++)
      |                    ~~^~~~~~~~~~~~~
./Main.cpp: In function ‘std::string move_cell(std::vector<std::pair<int, int> >, std::vector<std::vector<int> >&, std::vector<std::vector<int> >&, int&, int&)’:
./Main.cpp:239:22: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<std::pair<int, int> >::size_type’ {aka ‘long unsigned int’} [-Wsign-compare]
  239 |     for(int i = 1; i < path.size(); i++)
      |                    ~~^~~~~~~~~~~~~
./Main.cpp: In function ‘int32_t main()’:
./Main.cpp:364:22: warning: comparison of integer expressions of different signedness: ‘std::__cxx11::basic_string<char>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
  364 |     while(ans.size() > t)
      |           ~~~~~~~~~~~^~~
In file included from /usr/include/c++/9/cassert:44,
                 from /usr/include/x86_64-linux-gnu/c++/9/bits/stdc++.h:33,
                 from ./Main.cpp:1:
./Main.cpp:366:23: warning: comparison of integer expressions of different signedness: ‘std::__cxx11::basic_string<char>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
  366 |     assert(ans.size() <= t);
      |            ~~~~~~~~~~~^~~~

Judge Result

Set Name test_ALL
Score / Max Score 19988760 / 50000000
Status
AC × 50
Set Name Test Cases
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt
Case Name Status Exec Time Memory
test_0000.txt AC 2502 ms 3492 KiB
test_0001.txt AC 2502 ms 3612 KiB
test_0002.txt AC 2503 ms 3492 KiB
test_0003.txt AC 2503 ms 3496 KiB
test_0004.txt AC 2503 ms 3632 KiB
test_0005.txt AC 2502 ms 3684 KiB
test_0006.txt AC 2503 ms 3660 KiB
test_0007.txt AC 2503 ms 3612 KiB
test_0008.txt AC 2503 ms 3656 KiB
test_0009.txt AC 2503 ms 3560 KiB
test_0010.txt AC 2502 ms 3608 KiB
test_0011.txt AC 2503 ms 3608 KiB
test_0012.txt AC 2503 ms 3632 KiB
test_0013.txt AC 2503 ms 3664 KiB
test_0014.txt AC 2503 ms 3668 KiB
test_0015.txt AC 2502 ms 3488 KiB
test_0016.txt AC 2502 ms 3548 KiB
test_0017.txt AC 2503 ms 3592 KiB
test_0018.txt AC 2503 ms 3556 KiB
test_0019.txt AC 2503 ms 3668 KiB
test_0020.txt AC 2502 ms 3624 KiB
test_0021.txt AC 2503 ms 3628 KiB
test_0022.txt AC 2503 ms 3548 KiB
test_0023.txt AC 2503 ms 3628 KiB
test_0024.txt AC 2503 ms 3688 KiB
test_0025.txt AC 2502 ms 3656 KiB
test_0026.txt AC 2502 ms 3552 KiB
test_0027.txt AC 2503 ms 3664 KiB
test_0028.txt AC 2503 ms 3612 KiB
test_0029.txt AC 2503 ms 3548 KiB
test_0030.txt AC 2502 ms 3540 KiB
test_0031.txt AC 2503 ms 3536 KiB
test_0032.txt AC 2503 ms 3688 KiB
test_0033.txt AC 2503 ms 3500 KiB
test_0034.txt AC 2503 ms 3692 KiB
test_0035.txt AC 2502 ms 3548 KiB
test_0036.txt AC 2502 ms 3684 KiB
test_0037.txt AC 2503 ms 3688 KiB
test_0038.txt AC 2503 ms 3560 KiB
test_0039.txt AC 2503 ms 3544 KiB
test_0040.txt AC 2502 ms 3556 KiB
test_0041.txt AC 2502 ms 3648 KiB
test_0042.txt AC 2503 ms 3540 KiB
test_0043.txt AC 2503 ms 3692 KiB
test_0044.txt AC 2503 ms 3620 KiB
test_0045.txt AC 2502 ms 3552 KiB
test_0046.txt AC 2503 ms 3548 KiB
test_0047.txt AC 2503 ms 3592 KiB
test_0048.txt AC 2502 ms 3660 KiB
test_0049.txt AC 2503 ms 3556 KiB