Codeforces 678E Another Sith Tournament 状压DP

题意:

\(n(n \leq 18)\)个人打擂台赛,编号从\(1\)\(n\),主角是\(1\)号。
一开始主角先选一个擂主,和一个打擂的人。
两个人之中胜的人留下来当擂主等主角决定下一个人打擂,败的人退出比赛,直到比赛只剩一个人。
已知任意两人之间决胜的胜率\(P_{ij}\),求主角最终能够获胜的概率。

分析:

\(d(S, i)\)表示存活的人的集合为\(S\),当前擂主为\(i \in S\),主角获胜的概率。
为了方便我们把编号设为\(0 \sim n-1\),递推边界\(d(1,0)=1\)

考虑\(d(S,i)\),枚举下一个要打擂的人\(k \in S\)

  • \(P_{ij}\)的概率\(i\)战胜\(j\),擂主为\(i\),状态转移到\(d(S-j,i)\)
  • \(P_{ji}\)的概率\(j\)战胜\(i\),擂主为\(j\),状态转移到\(d(S-i,j)\)

因为主角可以决定打擂人选\(j\),所以\(d(S,i)=max\{ P_{ij}d(S-j,i) + P_{ji}d(S-i,j) \}\)

最后枚举最开始的擂主,选一个最大值就是答案。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

double p[18][18], d[1 << 18][18];

int main()
{
    int n; scanf("%d", &n);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++) scanf("%lf", &p[i][j]);

    d[1][0] = 1;
    for(int S = 3; S < (1 << n); S += 2) {
        for(int i = 0; i < n; i++) if(S&(1<<i)) {
            for(int j = 0; j < n; j++) if(j != i && (S&(1<<j))) {
                d[S][i] = max(d[S][i], p[i][j]*d[S^(1<<j)][i] + p[j][i]*d[S^(1<<i)][j]);
            }
        }
    }

    double ans = 0;
    for(int i = 0; i < n; i++) ans = max(ans, d[(1<<n)-1][i]);

    printf("%.15f\n", ans);

    return 0;
}
posted @ 2016-06-15 15:13  AOQNRMGYXLMV  阅读(...)  评论(... 编辑 收藏