Channel (UVa Live Archive World Finals Harbin 2010)

http://livearchive.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=44&page=show_problem&problem=2790

問題

H*Wの盤面があって、そこに左下から右下へ運河を作る。運河の長さを最大化せよ。
答えは一意に定まる。
2<=H<=20
2<=W<=9

解法

一行分どう接続してるか記憶してメモ化再帰。あとはやるだけ。

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

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[30][30];
char ans[30][30];

inline int Encode(const char connect[10]) {
  int ret = 0;
  REP(i, w + 1) {
    ret |= (7 & connect[i]) << (i * 3);
  }
  return ret;
}

//int Decode(const ll code, int x) {
//  int ret = (code >> (x * 4)) & 15;
//  int v = -(ret >> 3);
//  ret |= v << 4;
//  return ret;
//}


int Calc(int y, int x, const char connect[10]);
void Fukugen(int y, int x, const char connect[10]);

void PrintConnect(const vector<char> &connect) {
  REP(i, w + 1) {
    cout << (int)connect[w - i] << " ";
  }
  cout << endl << endl;
}

int NextState(int y, int x, const char pconnect[10], int wall, int fukugen) {
  int ny = y;
  int nx = x + 1;
  if (nx == w) { nx = 0; ny = y + 1; }
  char nconnect[10];
  memcpy(nconnect + 1, pconnect, sizeof(nconnect) - sizeof(char));
  //rotate(nconnect, nconnect + w, nconnect + w + 1);
  int llc = pconnect[1];
  int lc = pconnect[0];
  int ruc = pconnect[w - 2];
  int uc = pconnect[w - 1];
  int luc = pconnect[w];
  if (x == 0) { lc = -1; luc = -1; }
  if (x <= 1) { llc = -1; }
  if (x == w - 1) { ruc = -1; }

  if (wall) {
    nconnect[0] = -1;
    // ...
    // .1#
    if (llc == -1 && luc == -1 && lc != -1 && (y != 0 || x != 1)) { return -1000; }

    // ?1?
    // .#.
    if ((y != 1 || x != 0) && uc >= 0) { return -1000; }

    // .1
    // 2#
    if (lc != -1 && luc == -1 && uc != -1) { return -1000; }

    // ???1.2
    // 33..#.
    if (luc < 0) { goto ok1; }
    REP(i, w) {
      if (luc == pconnect[i]) { goto ok1; }
    }
    return -1000;

ok1:
    // ????11.22
    // 333..#...
    if (uc < 0) { goto ok2; }
    REP(i, w - 1) {
      if (uc == pconnect[i]) { goto ok2; }
    }
    return -1000;
ok2:;
  } else {
    nconnect[0] = 7;
    if (lc == -2 || uc == -2) { return -1000; }
    // 1?2
    // ?C.
    if (luc != -1 && ruc != -1 && lc == -1) { return -1000; }

    // ??1
    // 1C.
    if (ruc != -1 && ruc == lc) { return -1000; }

    // 1.
    // .C
    if (lc == -1 && uc == -1 && luc != -1) { return -1000; }

    // ?1
    // 1C
    if (lc != -1 && lc == uc) { return -1000; }
    
    // 1111
    // C...
    if (y == 1 && x == 0 && ruc != -1) { return -1000; }

    if (lc == -1 && uc == -1) {
      // ..
      // .C
      nconnect[0] = 7;
      if (x == w - 1) { return -1000; }
    } else if (uc == -1) {
      // .?.
      // ?3C
      nconnect[0] = lc;
      if ((luc != -1 || llc != -1) && x != 0) { nconnect[1] = -2; }
    } else if (lc == -1) {
      // ?1?
      // .C.
      nconnect[0] = uc;
      if (y != 1 || x != 0) { nconnect[w] = -2; }
    } else {
      // .1 -> .1
      // 2C    1C
      nconnect[0] = -2;
      if (llc != -1) { nconnect[1] = -2; }
      else { nconnect[1] = uc; }
      if (y > 1) { nconnect[w] = -2; }
      REP(i, w + 1) {
        if (nconnect[i] == lc) { nconnect[i] = uc; }
      }
    }
  }
  
  // Normalize
  {
    int cnt = 0;
    char mapto[8];
    MEMSET(mapto, -1);
    for (int i = w; i >= 0; i--) {
      int v = nconnect[i];
      if (v == -1 || v == -2) {
        continue;
      } else if (mapto[v] != -1) {
        nconnect[i] = mapto[v];
      } else {
        nconnect[i] = cnt;
        mapto[v] = cnt++;
      }
    }
    //assert(cnt < 6);
  }

  if (y == 1 && x == 0 && nconnect[w] == -2) { return -1000; }
  if (y == 0 && x == w - 1 && nconnect[1] == -1 && !wall) { return -1000; }
  if (fukugen) { Fukugen(ny, nx, nconnect); return 0; }
  return Calc(ny, nx, nconnect);
}

map<int, char> memo[22][10];
int Calc(int y, int x, const char connect[10]) {
  if (y == h) {
    if (connect[0] != 0) { return -1000; }
    FOR(i, 1, w + 1) {
      if (connect[i] >= 0) { return -1000; }
    }
    return 0;
  }
  int code = Encode(connect);
  if (memo[y][x].count(code)) { return memo[y][x][code]; }
  int ret = -100;
  //cout << "Calc:" << endl;
  //cout << y << " " << x << " " << ret << endl;
  //PrintConnect(connect);
  if (field[y][x] == '#') {
    ret = NextState(y, x, connect, 1, 0);
  } else if ((y == h - 1 && x == w - 1) || (y == 0 && x == 0)) {
    ret = NextState(y, x, connect, 0, 0) + 1;
  } else {
    int nret1 = NextState(y, x, connect, 0, 0) + 1;
    int nret2 = NextState(y, x, connect, 1, 0);
    ret = max(ret, nret1);
    ret = max(ret, nret2);
  }
  ret = max(ret, -101);
  return memo[y][x][code] = ret;
}

void Fukugen(int y, int x, const char connect[10]) {
  //cout << y << " " << x << endl;
  if (y == h) { return; }
  if (field[y][x] == '#') {
    NextState(y, x, connect, 1, 1);
  } else if ((y == h - 1 && x == w - 1) || (y == 0 && x == 0)) {
    ans[y][x] = 'C';
    NextState(y, x, connect, 0, 1);
  } else {
    int nret1 = NextState(y, x, connect, 0, 0) + 1;
    int nret2 = NextState(y, x, connect, 1, 0);
    //assert(nret1 != nret2);
    if (nret1 > nret2) {
      ans[y][x] = 'C';
      NextState(y, x, connect, 0, 1);
    } else {
      NextState(y, x, connect, 1, 1);
    }
  }
}


int main() {
  int test_case = 0;
  while (scanf("%d %d", &h, &w), h|w) {
    test_case++;
    REP(y, 22) REP(x, 10) {
      memo[y][x].clear();
    }
    REP(y, h) {
      scanf("%s", field[y]);
    }
    memcpy(ans, field, sizeof(field));
    char ini[10];
    MEMSET(ini, -1);
    Calc(0, 0, ini);
    Fukugen(0, 0, ini);
    //cout << "Ans: " << Calc(0, 0, ini) << endl;
    printf("Case %d:\n", test_case);
    REP(y, h) {
      puts(ans[y]);
    }
    puts("");
  }
}