TopCoder SRM 663 Div2 Problem 1000 - CheeseRolling (狀壓dp)

題意

n個人,每個人對上誰都有勝負。現在問如何安排比賽,輸出每個人最後的勝利的情況數。

思路

唐老師原話。

點集爲 i 勝者爲 j 的方案數
實際上有用的狀態不多
i裏的點數是2的冪次
然後枚舉i的子集s,使得s點數是i的一半,t=i xor s,也就是補集,枚舉兩個子集裏的勝者,然後將方案數加到兩個勝者比賽後的勝者裏。

TC的官方題解

What we can do is represent this as f(i,S), where i is the player in question and S the set of players available in this tournament. We want to play with recursion so we need a way to divide the problem into smaller versions of this problem. Consider this: An elimination tournament of 16 players can be seen as two tournaments of 8 distinct players each plus a last match between the winners of the two smaller tournaments. When given |S| players, we want to divide them in two disjoint halves: S1 and S2, the only thing we’ll know about the order is that players in S2 will be ordered after S1. So S1 and S2 will each have one winner. We are interested in scenarios in which player i wins S1 or S2 (Depending of which one i was included in). There are two cases, in one of the cases i is in S1; There are f(i,S1) ways in which i will win S1. For each j in S2, it is possible that j wins the mini-tournament of S2 a total of f(j,S2) times. If according to our match up rules, i wins after matched against j, then we need to add f(i,S1)×f(j,S2) to the final result, because those are the number of ways in which i versus j is the final match in the tournament AND thus ways in which i wins. Consider the cases in which i is in S2. Due to symmetry, this will look exactly the same, so in fact we should be adding 2×f(i,S1)×f(j,S2) to the result.

簡單地說,對於某個狀態S,我們可以把它分成兩組,從每組裏面各找出一個人對決,加上之前的子狀態那個人贏的數目。

代碼

class CheeseRolling {
public:
    LL dp[(1<<17)][17], number_in_state[(1<<17)];
    vector<long long> waysToWin(vector<string> wins) {
        int n = SZ(wins);
        for (int i = 1; i < (1<<n); i++)
        {
            vector<int> cur_state;
            for (int j = 0; j < n; j++) if ((i>>j)&1)
                cur_state.push_back(j);
            int sz = SZ(cur_state);
            number_in_state[i] = sz;
            if (sz == 1) dp[i][cur_state[0]] = 1;
            if (sz != 2 && sz != 4 && sz != 8 && sz != 16) continue;
            for (int s = (i-1)&i; s; s = (s-1)&i)
            {
                if (number_in_state[s] != sz / 2) continue;
                vector<int> substate, remain_person;
                for (int j = 0; j < n; j++) if ((s>>j)&1)
                    substate.push_back(j);
                for (int j = 0; j < n; j++) if (!((s>>j)&1) && ((i>>j)&1))
                    remain_person.push_back(j);
                int ss = i^s;
                for (auto u : substate)
                    for (auto v : remain_person)
                    {
                        if (wins[u][v] == 'Y') dp[i][u] += dp[s][u] * dp[ss][v];
                        else dp[i][v] += dp[s][u] * dp[ss][v];
                    }
            }
        }
        vector<LL> ans;
        for (int i = 0; i < n; i++) ans.push_back(dp[(1<<n)-1][i]);
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章