mayoko’s diary

プロコンとかいろいろ。

SRM 515 div1 med:NewItemShop

解法

基本的に状態として[持ってる刀の数][時間][どの客が来たかのフラグ]というのを持ってdpすれば大丈夫です。
ただ,客が最高で24人いるわけなので,どの客が来たかのフラグというのは2^24通りになってこれでは状態数が多すぎます。

しかし,客が来たかのフラグは「ある客が来たらもう別の客は来ない」という図式が成り立っている客のグループだけ管理されていれば良いので,フラグは客のグループの数分だけあれば良いとわかります。グループの数はたかだか24/2 = 12グループしかないのでこうすると状態数は24*25*2^12となり計算が間に合います。

以下のソースコードは計算量的にかなり雑に書いてますがtopcoderのサーバは速いので問題ありません。

struct cust {
    int t, cost, p;
    int g;
};
vector<cust> customer;

double dp[25][24][1<<13];

double dfs(int rest, int t, int state) {
    if (t >= 24) return 0;
    if (rest == 0) return 0;
    bool exist = false;
    double& ret = dp[rest][t][state];
    if (ret >= 0) return ret;
    ret = 0;
    for (cust C : customer) {
        if (C.t != t) continue;
        if (C.g != -1 && ((state>>C.g) & 1)) break;
        exist = true;
        // 来る
        double sum = 100;
        if (C.g != -1) {
            for (cust CC : customer) {
                if (CC.g == C.g && CC.t < C.t) sum -= CC.p;
            }
        }
        {
            int nstate = state;
            if (C.g != -1) nstate |= (1<<C.g);
            ret = max(C.cost+dfs(rest-1, t+1, nstate), dfs(rest, t+1, nstate));
            ret *= C.p/sum;
        }
        // 来ない
        {
            ret += (sum-C.p)/sum * dfs(rest, t+1, state);
        }
        break;
    }
    if (!exist) return ret = dfs(rest, t+1, state);
    return ret;
}

class NewItemShop {
public:
    double getMaximum(int swords, vector <string> customers) {
        for (int i = 0; i < 25; i++) for (int j = 0; j < 24; j++) {
            for (int k = 0; k < (1<<13); k++) dp[i][j][k] = -1;
        }
        int group = 0;
        customer.clear();
        for (string s : customers) {
            vector<cust> v;
            int cur = 0;
            while (cur < s.size()) {
                int now = 0;
                cust C;
                C.g = -1;
                while (s[cur] != ',' && cur < s.size()) {
                    now = now*10 + s[cur]-'0';
                    cur++;
                }
                C.t = now;
                cur++;
                now = 0;
                while (s[cur] != ',' && cur < s.size()) {
                    now = now*10 + s[cur]-'0';
                    cur++;
                }
                C.cost = now;
                cur++;
                now = 0;
                while (s[cur] != ' ' && cur < s.size()) {
                    now = now*10 + s[cur]-'0';
                    cur++;
                }
                C.p = now;
                v.push_back(C);
                cur++;
            }
            if (v.size() > 1) {
                for (cust &C : v) {
                    C.g = group;
                }
                group++;
            }
            for (cust C : v) customer.push_back(C);
        }
        //        for (cust C : customer) {
        //            cout << C.t << "  " << C.cost << "  " << C.p << "  " << C.g << endl;
        //        }
        return dfs(swords, 0, 0);
    }
};