AOJ 0581 Gifts

問題

日本語なので本文参照(http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0581


h, wのグリッドの左上から右下まで、右または下に移動することを繰り返して到達する。
#のマスには進入できず、数字のマスに進入すると、
最初の一回だけその数字の分だけ得点が得られる。


k回だけ、上または左に戻ることができるとき、
得られる得点の最大値を求めよ。

制約条件

h, w≦50
k≦3

方針

現在のマスの周囲のうち、すでに訪れたマスを全部覚えておけばよい。
kが小さいので、二度踏む可能性がある(=覚える必要がある)マスというのは少ない。
具体的には11個以下になる。


なので、dp[i][j][k][bit]を(i, j)にいて、k回戻って、bitで表される周囲のマスに既に入っているときの最大スコアとして、
それを更新してくDPをすればいい。

ソースコード

const int dy[] = {0, 1, 0, -1}, dx[] = {1, 0, -1, 0};
int h, w, K;
string in[50];
int dp[50][50][4][1 << 11];

vector<pi> pos[4];
int trans[1 << 11][4][4];
int vis[1 << 11][4][4];

int main(){
	//kごとに、覚えるべきマスを列挙
	rep(k, 4){
		for(int y = -3; y < 4; y++)
		for(int x = -3; x < 4; x++) if(y || x){
			int yoru = max(x, 0) + max(y, 0);
			int modoru = max(-x, 0) + max(-y, 0);
			//(i, j)まで来る途中にそこに寄り道するのに必要なコスト
			//(i, j)からゴールに行くまでにそこに寄るのに必要なコスト
			//これがどちらもkに関する制約を満たす必要がある
			if(yoru <= k && modoru < 4 - k) pos[k].pb(mp(y, x));
		}
	}
	//移動でビットがどう変わるか
	rep(bit, 1 << 11) rep(k, 4) rep(d, 4){
		
		int nk = k + (d > 1);
		if(nk > 3) continue;
		int &nbit = trans[bit][k][d];
		
		for(int y = -3; y < 4; y++)
		for(int x = -3; x < 4; x++){
			int pp = -1, p = -1;
			int ny = y - dy[d], nx = x - dx[d];
			
			rep(i, pos[k].size()) if(pos[k][i] == mp(y, x)) pp = i;
			rep(i, pos[nk].size()) if(pos[nk][i] == mp(ny, nx)) p = i;
			if(pp < 0 || p < 0) continue;
			if(bit & 1 << pp) nbit |= 1 << p;
		}
		//直前にいたマスにフラグをたてる
		int t = -1;
		rep(i, pos[nk].size()) if(pos[nk][i] == mp(-dy[d], -dx[d])) t = i;
		if(t >= 0) nbit |= 1 << t;

		rep(i, pos[k].size()) if((bit & 1 << i) && pos[k][i] == mp(dy[d], dx[d])) vis[bit][k][d] = 1;
	}
	
	cin >> h >> w >> K;
	rep(i, h) cin >> in[i];
	memset(dp, -1, sizeof(dp));
	
	dp[0][0][0][0] = 0;
	rep(k, K + 1) rep(i, h) rep(j, w) rep(l, 1 << 11) if(dp[i][j][k][l] >= 0){
		
		rep(d, 4){
			int nk = k + (d > 1);
			if(nk > K) continue;
			
			int nxt = dp[i][j][k][l], nl = trans[l][k][d];
			int ny = i + dy[d], nx = j + dx[d];
			if(ny < 0 || ny >= h || nx < 0 || nx >= w || in[ny][nx] == '#') continue;
			
			if(!vis[l][k][d] && in[ny][nx] != '.') nxt += in[ny][nx] - '0';
			dp[ny][nx][nk][nl] = max(dp[ny][nx][nk][nl], nxt);
		}
	}
	int ans = -1;
	rep(i, K + 1) rep(j, 1 << 11) ans = max(ans, dp[h - 1][w - 1][i][j]);
	cout << ans << endl;
	
	return 0;
}

出展

2013年度日本情報オリンピック(JOI)予選