TopCoder SRM588 Div1 Med 450 KeyDungeonDiv1

問題文

概要

鍵穴がいくつかついた扉がいくつかある. それぞれの扉の先には, いくつかの鍵が置いてある.

鍵穴は赤と緑のニ種類がある. 一方, 鍵は赤と緑と白の三種類がある.

赤や緑の鍵は, それぞれその色の鍵穴にのみ使える. 白は両方に使える.

扉を開けると, 使った鍵はこわれてしまう.

最初に所持している鍵と, 扉の情報が与えられるので, 所持している, 壊れていない鍵の合計数を最大化せよ.

制約

$ 1 \le \mathit{doors} \le 12,\ 1 \le \mathit{doorR}, \mathit{doorG}, \mathrm{e.t.c} \le 10 $.

解法

BitDP する.

開けた扉を知っていると, 今持っている鍵の個数がわかる. あとは, 赤緑白のうち二つの情報がわかればよい.

白い鍵はなるべく多くしたいので, dp[開けた扉][赤] = max 白 のようにすればよい.

ソースコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 358.87 pts

int KeyDungeonDiv1::maxKeys( vector <int> doorR, vector <int> doorG, vector <int> roomR, vector <int> roomG, vector <int> roomW, vector <int> keys ){
    const int n = size(doorR);
    vector<vector<int>> dp = vector<vector<int>>(1<<n, vector<int>(140, -(1<<30)));
    dp[0][keys[0]] = keys[2];

    int res = 0;

    rep(A, 1<<n) rep(r, 140) if(dp[A][r] >= 0){
        int sum = keys[0] + keys[1] + keys[2];
        rep(i, n) if(A>>i&1){
            sum += roomR[i] - doorR[i];
            sum += roomG[i] - doorG[i];
            sum += roomW[i];
        }
        chmax(res, sum);
        int nowR = r, nowW = dp[A][r], nowG = sum - nowR - nowW;

        rep(i, n) if(!(A>>i&1)){
            int useR = min(nowR, doorR[i]);
            int useG = min(nowG, doorG[i]);
            int useW = (doorR[i] - useR) + (doorG[i] - useG);
            if(useW > nowW) continue;
            int nexR = nowR - useR + roomR[i];
            int nexG = nowG - useG + roomG[i];
            int nexW = nowW - useW + roomW[i];
            chmax(dp[A|(1<<i)][nexR], nexW);
        }
    }
    return res;
}