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.