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.
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;
}