落書きの魔女 (AOJ 2314)

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2314

問題

解法

解説参照。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>
#include <map>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const double EPS = 1e-9;
static const double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))

int h, w;
char field[20][20];
map<vector<char>, int> memo[20][20];

char mapto[10];
int calc(vector<char> connect, int x, int y);

int NextState(vector<char> connect, int x, int y, char c) {
  int ret = 0;
  int pc = connect[x];
  int lc = -1;
  if (x != 0) { lc = connect[x - 1]; }
  if (c == '#') {
    ret++;
    if (lc >= 0 && lc == pc) { return -1; }
    if (lc >= 0) {
      connect[x] = lc;
      if (pc >= 0) {
        REP(i, w) {
          if (connect[i] == pc) { connect[i] = lc; }
        }
      }
    } else if (pc == -1) {
      connect[x] = 9;
    } else {
      // do nothing
    }
  } else {
    if ((y != 0 && pc == -1) ||
        (x != 0 && lc == -1)) { return -1; }
    connect[x] = -1;
    if (pc != -1) {
      REP(i, w) {
        if (connect[i] == pc) { goto ok; }
      }
      return -1;
ok:;
    }
  }

  {
    // Normalize
    MEMSET(mapto, -1);
    int m = 0;
    REP(i, w) {
      if (connect[i] != -1) {
        if (mapto[(int)connect[i]] == -1) {
          mapto[(int)connect[i]] = m++;
        }
        connect[i] = mapto[(int)connect[i]];
      }
    }
    assert(m <= 8);
  }
  int nret = calc(connect, x + 1, y);
  if (nret == -1) { ret = -1; }
  else { ret += nret; }
  return ret;
}

int calc(vector<char> connect, int x, int y) {
  if (memo[y][x].count(connect)) { return memo[y][x][connect]; }
  if (y == h) {
    REP(x, w) {
      if (connect[x] > 0) { return -1; }
    }
    return 0;
  }
  if (x == w) { return calc(connect, 0, y + 1); }
  int ret = -1;
  if (field[y][x] == '?') {
    ret = max(ret, NextState(connect, x, y, '#'));
    ret = max(ret, NextState(connect, x, y, '.'));
  } else {
    ret = max(ret, NextState(connect, x, y, field[y][x]));
  }
  return memo[y][x][connect] = ret;
}

int main() {
  while (scanf("%d %d", &h, &w) > 0) {
    REP(y, 20) REP(x, 20) { memo[y][x].clear(); }
    REP(y, h) {
      int v = scanf("%s", field[y]);
      assert(v == 1);
    }
    int ans = -1;
    if (w == 1) {
      int cnt = 0;
      REP(y, h) { if (field[y][0] != '.') { cnt++; } }
      if (h == 2) { if (cnt != 0) { ans = cnt; } }
      else {
        ans = cnt;
        FOR(y, 1, h - 1) {
          if (field[y][0] == '.') { ans = -1; }
        }
      }
    } else {
      vector<char> connect(w, -1);
      ans = calc(connect, 0, 0);
    }
    printf("%d\n", ans);
  }
}