Analysis: What's Up With Gravity? by Fatih Gelgi
This is obviously a shortest path problem. We can solve the problem using Dijkstra's shortest path algorithm with a small twist. In this case, nodes are the cells in the grid. There is an edge between two nodes (a,b) if b can be reached by left, right moves or flipping the gravity from a. Notice that it is only necessary to keep the "stable cells" as nodes - the cells that CB doesn't fall down in one of the gravity directions. Since there can be at most O(N^2) edges, running time is O(N^2 log N^2) using a priority queue.
Since the problem is a shortest path problem and the edges can be 0 or 1 distance, we can also solve the problem modifying BFS. Remember that the distance of a node has to be always smaller than another node which is added later in a BFS queue. Hence, we have to add the queue all reachable nodes (via left and right moves) in the same flip. To find all reachable nodes from a node, we can use either DFS or BFS. Since each cell is visited only O(1) time, the total running time is O(N^2) which is slightly faster than the former solution.
A sample solution with Dijkstra is as follows:
#include <iostream> #include <fstream> #include <queue> using namespace std; #define MAX 502 int n,m,mark[MAX][MAX]; string mat[MAX]; struct Point { int y,x; }; // comparison criteria for priority queue bool operator<(Point a,Point b) { // smaller number of flips first return mark[a.y][a.x]>mark[b.y][b.x]; } priority_queue<Point> q; // falling down in the given direction dir={-1:up,1:down} Point fall(Point p,int dir) { for (; ; p.y+=dir) { if (mat[p.y][p.x]=='D') break; if (p.y+dir<0 || p.y+dir>=n) return {-1,-1}; if (mat[p.y+dir][p.x]=='#') break; } return p; } // make move k={-1:left,0:flip,1:right} // in the given direction dir={-1:up,1:down} Point action(Point p,int k,int dir) { // move left or right if (k) { p.x+=k; if (p.x<0 || p.x>=m || mat[p.y][p.x]=='#') return {-1,-1}; } // flip else dir=-dir; return fall(p,dir); } int main() { ifstream fin("gravity.in"); fin >> n >> m; for (int i=0; i<n; i++) fin >> mat[i]; fin.close(); Point c,d; for (int i=0; i<n; i++) for (int j=0; j<m; j++) if (mat[i][j]=='C') c=fall({i,j},1); else if (mat[i][j]=='D') d={i,j}; if (c.y>=0) { q.push(c); mark[c.y][c.x]=1; for ( ; !q.empty(); ) { Point e=q.top(); q.pop(); if (e.y==d.y && e.x==d.x) break; // add legal moves to the priority queue for (int i=-1,dir=mark[e.y][e.x]%2 ? 1 : -1; i<=1; i++) { Point p=action(e,i,dir); if (p.y!=-1 && !mark[p.y][p.x]) // add if not visited before { mark[p.y][p.x]=mark[e.y][e.x]; if (!i) mark[p.y][p.x]++; // if the move is a flip q.push(p); } } } } ofstream fout("gravity.out"); fout << mark[d.y][d.x]-1 << endl; fout.close(); }
BFS implementation sample is below:
#include <iostream> #include <fstream> #include <queue> using namespace std; #define MAX 502 #define DIR(e) (mark[e.y][e.x]%2 ? 1 : -1) int n,m,mark[MAX][MAX]; string mat[MAX]; struct Point { int y,x; } c,d; queue<Point> q; // falling down in the given direction dir={-1:up,1:dowm} Point fall(Point p,int dir) { for (; ; p.y+=dir) { if (mat[p.y][p.x]=='D') break; if (p.y+dir<0 || p.y+dir>=n) return {-1,-1}; if (mat[p.y+dir][p.x]=='#') break; } return p; } // make move k={-1:left,0:flip,1:right} // in the given direction dir={-1:up,1:down} Point action(Point p,int k,int dir) { // move left or right if (k) { p.x+=k; if (p.x<0 || p.x>=m || mat[p.y][p.x]=='#') return {-1,-1}; } // flip else dir=-dir; return fall(p,dir); } // try all points that has same flip using DFS int dfs(Point p,int flip) { if (p.y==-1 || mark[p.y][p.x]) return 0; mark[p.y][p.x]=flip; if (p.y==d.y && p.x==d.x) return 1; q.push(p); int dir=DIR(p); return dfs(action(p,-1,dir),flip) || dfs(action(p,1,dir),flip); } int main() { ifstream fin("gravity.in"); fin >> n >> m; for (int i=0; i<n; i++) fin >> mat[i]; fin.close(); for (int i=0; i<n; i++) for (int j=0; j<m; j++) if (mat[i][j]=='C') c=fall({i,j},1); else if (mat[i][j]=='D') d={i,j}; // BFS if (!dfs(c,1)) for (; !q.empty(); ) { Point e=q.front(); q.pop(); Point p=action(e,0,DIR(e)); // flip the gravity // push all the points that has the same flip if (dfs(p,mark[e.y][e.x]+1)) break; } ofstream fout("gravity.out"); fout << mark[d.y][d.x]-1 << endl; fout.close(); }Author's Note: This problem is inspired from the game VVVVVV. The problem was initially called BBBBBB but it was decided that was too confusing.