1168 Off Balance (Problem D: ぐらぐら)

http://rose.u-aizu.ac.jp/onlinejudge/ProblemSet/description.jsp?id=1168&lang=jp
本番中に解けなかった問題.
入力がひどい(と思う)ので一旦それぞれのピースにユニークな id を割り振ってから解いた.
check の型が若干アレだが,start の地点にあるピースの重心が (left, right) の区間に入っているかどうか判定し,その結果とそのピースまでの重心と質量(= ブロックの数)を返している.
無駄に長い…
id を割り振るときと割り振った後で2回 BFS しているのが無駄だが,1ピースは4つのブロックしか持たないのでまぁこれでもいいかとか.

#include <iostream>
#include <vector>
#include <queue>
#include <complex>
#include <map>
using namespace std;
typedef complex<int> P;

vector<vector<int> > convert(const vector<string>& grid)
{
  const int h = grid.size(), w = grid[0].size();
  vector<vector<int> > ret(h, vector<int>(w, 0));

  int id = 1;
  for (int i = 0; i < h; i++) {
    for (int j = 0; j < w; j++) {
      if (grid[i][j] != '.' && ret[i][j] == 0) {
        queue<P> q;
        q.push(P(i, j));
        const char n = grid[i][j];
        ret[i][j] = id;
        while (!q.empty()) {
          const P p = q.front();
          q.pop();
          for (int d = 0; d < 4; d++) {
            static const P dir[] = {P(-1, 0), P(1, 0), P(0, -1), P(0, 1)};
            const P next = p + dir[d];
            if (0 <= next.real() && next.real() < h && 0 <= next.imag() && next.imag() < w
                && grid[next.real()][next.imag()] == n && ret[next.real()][next.imag()] == 0) {
              q.push(next);
              ret[next.real()][next.imag()] = id;
            }
          }
        }
        id++;
      }
    }
  }
  return ret;
}

pair<bool,pair<double,int> > check(vector<vector<int> >& grid, const P& start, int left, int right)
{
  const int h = grid.size(), w = grid[0].size();

  vector<P> points;
  queue<P> q;
  q.push(start);
  points.push_back(start);
  const int id = grid[start.real()][start.imag()];
  grid[start.real()][start.imag()] = 0;
  double m = start.imag()+0.5;
  while (!q.empty()) {
    const P p = q.front();
    q.pop();
    for (int d = 0; d < 4; d++) {
      static const P dir[] = {P(-1, 0), P(1, 0), P(0, -1), P(0, 1)};
      const P next = p + dir[d];
      if (0 <= next.real() && next.real() < h && 0 <= next.imag() && next.imag() < w
          && grid[next.real()][next.imag()] == id) {
        m += double(next.imag()) + 0.5;
        q.push(next);
        points.push_back(next);
        grid[next.real()][next.imag()] = 0;
      }
    }
  }

  map<int, pair<P, pair<int,int> > > children;
  for (int i = 0; i < 4; i++) {
    const int x = points[i].real(), y = points[i].imag();
    if (x > 0 && grid[x-1][y] != 0) {
      map<int, pair<P,pair<int,int> > >::iterator it = children.find(grid[x-1][y]);
      if (it == children.end()) {
        children.insert(make_pair(grid[x-1][y], make_pair(P(x-1, y), make_pair(y, y+1))));
      } else {
        it->second.second.first = min(it->second.second.first, y);
        it->second.second.second = max(it->second.second.second, y+1);
      }
    }
  }

  int cnt = 4;
  for (map<int,pair<P,pair<int,int> > >::const_iterator it(children.begin()); it != children.end(); ++it) {
    pair<bool, pair<double,int> > r = check(grid, it->second.first, it->second.second.first, it->second.second.second);
    if (!r.first) {
      return make_pair(false, make_pair(m, cnt));
    } else {
      m += r.second.first;
      cnt += r.second.second;
    }
  }
  const double g = m / cnt;
  return make_pair(left < g && g < right, make_pair(m, cnt));
}

int main()
{
  int w, h;
  while (cin >> w >> h && !(w == 0 && h == 0)) {
    vector<string> grid(h);
    for (int i = 0; i < h; i++) {
      cin >> grid[i];
    }

    P start;
    int left = w, right = 0;
    for (int j = 0; j < w; j++) {
      if (grid.back()[j] != '.') {
        start = P(h-1, j);
        left = min(left, j);
        right = max(right, j+1);
      }
    }
    vector<vector<int> > g = convert(grid);

    if (check(g, start, left, right).first) {
      cout << "STABLE" << endl;
    } else {
      cout << "UNSTABLE" << endl;
    }
  }
  return 0;
}