SRM417: CubeNets

これまた難しい問題だったけど、綺麗な解法を思い浮かんで気持ちよかった。

(問題概要)
立方体の展開図が文字列で与えられる。
立方体の展開図として正しいかどうか判断せよ。

(解法)
サイコロの展開図として考える。
展開図にそってサイコロを転がして、接地面の数字を割り当てていく。
このとき以下の矛盾がなければよい。

  • 同じ数字が2箇所ある
  • 同じ場所に2通りの数字の振り方がある

私のサイコロライブラリはゴリ押しなので汚いですがコード。

#include <cstdio>
#define IN(x,s,g) ((x) >= (s) && (x) < (g))
#define ISIN(x,y,w,h) (IN((x),0,(w)) && IN((y),0,(h)))

#define REP(i,n) for(int i=0; i<(int)(n); i++)

#include <queue>
#include <set>
#include <iostream>
#include <sstream>

using namespace std;

class CubeNets {
public:
  string isCubeNet(vector <string> figure);
};

class Dice{
public:
  enum direction{
    UP    = 0,
    RIGHT = 1,
    DOWN  = 2,
    LEFT  = 3,
    RRIGHT= 4,
    RLEFT = 5
  };

  /*
   *   6
   *   2
   * 3 1 4
   *   5
   */

private:
  static int next_top[7][7][6];
  static int next_right[7][7][6];
  static int front[7][7];

  int _top;
  int _right;

  static void initDie(){
    front[1][3] = 2; front[1][2] = 4; front[1][4] = 5; front[1][5] = 3;
    front[2][1] = 3; front[2][3] = 6; front[2][4] = 1; front[2][6] = 4;
    front[3][1] = 5; front[3][2] = 1; front[3][5] = 6; front[3][6] = 2;
    for(int i = 4; i < 7; i++){
      for(int j = 1; j < 7; j++){
        if(i == j || i + j == 7) continue;
        int t = 7 - i;
        int l = 7 - j;
        front[i][j] = front[t][l];
      }
    }

    for(int t = 1; t < 7; t++){
      for(int r = 1; r < 7; r++){
        if(t == r || t + r == 7) continue;

        next_top[t][r][UP]     = front[t][r];
        next_top[t][r][LEFT]   = r;
        next_top[t][r][RIGHT]  = 7 - r;
        next_top[t][r][DOWN]   = 7 - front[t][r];
        next_top[t][r][RRIGHT] = t;
        next_top[t][r][RLEFT]  = t;

        next_right[t][r][UP]     = r;
        next_right[t][r][LEFT]   = 7 - t;
        next_right[t][r][RIGHT]  = t;
        next_right[t][r][DOWN]   = r;
        next_right[t][r][RRIGHT] = 7 - front[t][r];
        next_right[t][r][RLEFT]  = front[t][r];
      }
    }
  }

  static void init(){
    static bool _init = false;

    if(!_init){
      initDie();
      _init = true;
    }
  }

public:
  Dice(int t = 1, int r = 3) : _top(t), _right(r) {
    init();
  }

  int getTop() const{ return _top; }
  int getRight() const{ return _right; }
  int getButtom() const{ return 7 - _top; }
  int getLeft() const{ return 7 - _right; }
  int getFront() const{ return front[_top][_right]; }
  int getBack() const{ return 7 - front[_top][_right]; }

  void next(int dir){
    int tmp = _top;
    _top     = next_top[_top][_right][dir];
    _right   = next_right[tmp][_right][dir];
  }

  void right() { next(RIGHT);  }
  void left()  { next(LEFT);   }
  void up()    { next(UP);     }
  void down()  { next(DOWN);   }
  void rright(){ next(RRIGHT); }
  void rleft() { next(RLEFT);  }

  static void forceInit(){ init(); }

  static int nextTop(int t, int r, int d){
    return next_top[t][r][d];
  }

  static int nextRight(int t, int r, int d){
    return next_right[t][r][d];
  }

};

int Dice::next_right[7][7][6] = {{{0}}};
int Dice::next_top[7][7][6]   = {{{0}}};
int Dice::front[7][7]         = {{0}};

bool solve(const vector<string> &f){
  const int h = f.size();
  const int w = f[0].size();
  vector<vector<int> > g(h, vector<int>(w));
  int sx, sy;

  REP(i, h){
    REP(j, w){
      if(f[i][j] == '#'){
	sx = j; sy = i;
	goto proc;
      }
    }
  }
 proc:;

  int cnt = 0;
  typedef pair<int, pair<int, Dice> > data;
  vector<int> use(7);
  queue<data> q;
  q.push(make_pair(sx, make_pair(sy, Dice())));

  while(q.size()){
    const data dt = q.front(); q.pop();
    const int x = dt.first;
    const int y = dt.second.first;
    const Dice d = dt.second.second;

    if(g[y][x] != 0){
      if(d.getButtom() != g[y][x])
	return false;
      else
	continue;
    }else{
      if(use[d.getButtom()]) return false;
      g[y][x] = d.getButtom();
      use[d.getButtom()] = 1;
      cnt++;
    }

    if(ISIN(x, y - 1, w, h) && f[y - 1][x] == '#'){
      Dice dd = d;
      dd.up();
      q.push(data(x, make_pair(y - 1, dd)));
    }

    if(ISIN(x, y + 1, w, h) && f[y + 1][x] == '#'){
      Dice dd = d;
      dd.down();
      q.push(data(x, make_pair(y + 1, dd)));
    }

    if(ISIN(x - 1, y, w, h) && f[y][x - 1] == '#'){
      Dice dd = d;
      dd.left();
      q.push(data(x - 1, make_pair(y, dd)));
    }

    if(ISIN(x + 1, y, w, h) && f[y][x + 1] == '#'){
      Dice dd = d;
      dd.right();
      q.push(data(x + 1, make_pair(y, dd)));
    }
  }

  return cnt == 6;
}

string CubeNets::isCubeNet(vector <string> figure){
  return solve(figure) ? "YES" : "NO";
}