TopCoder SRM 588 Div1 Medium KeyDungeonDiv1

問題

n個のドアがある。
i番目のドアを開けるのに、赤の鍵がdoorR[i]個、緑の鍵がdoorG[i]個必要である。
i番目のドアを開けると、(最初の一回だけ)
赤の鍵がroomR[i]個、緑の鍵がroomG[i]個、白の鍵がroomW[i]個得られる。


一度使った鍵は壊れるのですぐに捨てなければならない。
白の鍵は、赤の鍵または緑の鍵の代わりとして使うことができる。
最初、赤の鍵をkeys[0]個、緑の鍵をkeys[1]個、白の鍵をkeys[2]個持っている。


途中、最大でいくつの鍵を同時に持つことができるか(種類は関係ない)求めよ。

制約条件

n≦12
keys, doorX, roomX≦10

方針

開けたドアの集合と、それまでに使った白の鍵の数を決めると、
今の鍵の個数が一意に定まる。
なのでこれを更新するような探索をすればいい。


ビットは単調増加(開けたドアは減らない)なのでDPで書ける。
今もっている鍵の個数もDPテーブルの値に持たせてしまうと楽。

ソースコード

int n;
pair<int, pi> dp[1 << 12][150];

class KeyDungeonDiv1 {
	public:
	int maxKeys(vector <int> doorR, vector <int> doorG, vector <int> roomR, vector <int> roomG, vector <int> roomW, vector <int> keys) {
		n = doorG.size();
		int ans = keys[0] + keys[1] + keys[2];
		
		rep(i, 1 << n) rep(j, 150) dp[i][j] = mp(-1, mp(0, 0));
		dp[0][0] = mp(keys[0], mp(keys[1], keys[2]));
		
		rep(i, 1 << n) rep(j, 150){
			int r = dp[i][j].first, g = dp[i][j].second.first, w = dp[i][j].second.second;
			if(r < 0) continue;
			
			rep(k, n) if(!(i & 1 << k)){
				int nw = w, nr = r, ng = g, nj = j;
				nw -= max(0, doorR[k] - r); nr = max(0, r - doorR[k]);
				nw -= max(0, doorG[k] - g); ng = max(0, g - doorG[k]);
				
				if(nw < 0) continue;
				nj += w - nw;
				nw += roomW[k];
				nr += roomR[k];
				ng += roomG[k];
				dp[i | 1 << k][nj] = max(dp[i | 1 << k][nj], mp(nr, mp(ng, nw)));
				if(nw + nr + ng > ans) ans = nw + nr + ng;
			}
		}
		return ans;
	}
};