Editorial for Optimal Raceway


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.

Submitting an official solution before solving the problem yourself is a bannable offence.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
#define N 100005
#define pb push_back
#define dbg(x) cerr << #x << ": " << x << endl;

vector<string> map2d;

int rows, cols;
int startIdx, endIdx;

vector<vector<pair<int, int>>> adj; // adjacency list of (node, weight) pairs
vector<int> dist;                   // distance array
vector<int> parent;                 // parent array (i.e., shortest path tree)

int findDegreeBetweenIdx(int idx1, int idx2){
    int row1 = idx1 / cols;
    int col1 = idx1 % cols;
    int row2 = idx2 / cols;
    int col2 = idx2 % cols;

    int x = col2 - col1;
    int y = -(row2 - row1);
    int deg = atan2(y, x) * 180 / M_PI;
    deg -= 90;
    deg *= -1;
    if(deg <= -180){
        deg += 360;
    }
    if(deg > 180){
        deg -= 360;
    }
    return deg;
}

void spinStill(int idx){
    int cur = idx;
    int gridSize = rows*cols;
    for(int i=0; i<8; i++){
        int next = cur - gridSize;
        if(next<0)next+=8*gridSize;
        adj[cur].push_back({next, 1});
        adj[next].push_back({cur, 1});
        cur = next;
    }
}

void addEdge(int u, int v){
    int final_dir = findDegreeBetweenIdx(u, v)/45 + 3;

    int left_dir = final_dir == 0 ? 7 : final_dir - 1;
    int right_dir = final_dir == 7 ? 0 : final_dir + 1;
    int gridSize = rows * cols;

    int straight = u + final_dir * gridSize;
    int left = u + left_dir * gridSize;
    int right = u + right_dir * gridSize;

    int dest = v + final_dir * gridSize;

    adj[straight].push_back({dest, 0});
    adj[left].push_back({dest, 1});
    adj[right].push_back({dest, 1});
}
int end_dir = -1;
int maxLen = 0;
int bfs01(int source, int target)
{
    int up_vector = 3 * rows * cols;
    deque<int> q;                  // double-ended queue for BFS
    dist.assign(adj.size(), numeric_limits<int>::max()); // initialize all distances to INF
    parent.assign(adj.size(), -1); // initialize all parents to -1
    dist[source + up_vector] = 0;  // distance from source to itself is 0
    q.push_front(source + up_vector); // add source to the queue

    while (!q.empty())
    {
        int u = q.front(); // get the next node from the queue
        maxLen = max(maxLen, (int)q.size());
        int rc = rows * cols;
        q.pop_front();

        if ((u%rc) == target) { // if we've reached the target node, we're done
            end_dir = u;
            return dist[u];
        }

        for (auto [v, w] : adj[u])
        { // iterate over the neighbors of u
            if (dist[u] + w < dist[v])
            {                          // if we can relax the edge from u to v
                dist[v] = dist[u] + w; // update the distance to v
                parent[v] = u;         // set u as the parent of v in the shortest path tree
                if (w == 0)
                { // if the weight of the edge is 0, add v to the front of the queue
                    q.push_front(v);
                }
                else
                { // otherwise, add v to the back of the queue
                    q.push_back(v);
                }
            }
        }
    }

    return -1; // if we get here, then we never found the target node
}

// Section for printing the path
vector<bool> usedNodes;

int tracePath(int end){
    int curr = end;
    int rc = rows * cols;
    int count = 0;

    usedNodes.resize(rc, 0);
    while((curr%rc) != startIdx){
        if(usedNodes[curr%rc]==0)
        count++;
        usedNodes[curr%rc] = 1;
        curr = parent[curr];
    }
    return count+1;
}

void print_map() {
    // Print the map
    for (int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {
            if(usedNodes[i*cols+j] == 1 && map2d[i][j] == '.'){
                cout << "▉";
            }else{
                cout << map2d[i][j];
            }
        }
        cout << endl;
    }
}
// end section

void construct_graph() {
    // Resize the adjacency list to the number of nodes for each direction
    adj.resize(rows * cols * 8);

    // Connect free cells to their eight neighbors
    for (int row = 1; row < rows-1; row++) {
        for (int col = 1; col < cols-1; col++) {
            int curr_idx = row * cols + col;
            spinStill(curr_idx);
            if (map2d[row][col] != '#') {
                for (int i = -1; i <= 1; i++) {
                    for (int j = 0; j <= 1; j++) {
                        // only add edges to 4 neighbors, since the graph is undirected
                        if (i == -1 && j == 0) {
                            continue;
                        }
                        if (i == 0 && j == 0) {
                            continue;
                        }
                        int next_row = row + i;
                        int next_col = col + j;
                        if (map2d[next_row][next_col] != '#') {
                            int next_idx = next_row * cols + next_col;
                            addEdge(curr_idx, next_idx);
                            addEdge(next_idx, curr_idx);
                        }
                    }
                }
            }
        }
    }
}

void fetch_map() {
    cin >> rows >> cols;
    cin.ignore(); // Ignore the newline character left in the input stream

    map2d.resize(rows);

    for (int i = 0; i < rows; i++) {
        getline(cin, map2d[i]);
    }
}

void infer_start_end() {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (map2d[i][j] == 'S') {
                startIdx = i * cols + j;
            }
            if (map2d[i][j] == 'E') {
                endIdx = i * cols + j;
            }
        }
    }
}

void solve() {
    int dist = bfs01(startIdx, endIdx);
    cout << dist << endl;
    // cout << "Distance: " << dist << endl;
    // if(dist == -1){
    //     cout << "No path found" << endl;
    //     return;
    // }
    // int length_of_track = tracePath(end_dir);
    // cout << "Length of track: " << length_of_track << endl;
    // cout << "Longest deque size: " << maxLen << endl;
    // print_map();
}

int32_t main() {
    cin.tie(0);
    ios_base::sync_with_stdio(NULL);
    fetch_map();
    infer_start_end();
    construct_graph();
    solve();
    return 0;
}