[POJ 3071]Football[概率DP]

[POJ 3071]Football[概率DP]

题目大意

\(2^n\) 个编号为 \(1, 2, 3, \ldots, 2^n\) 的队伍进行足球比赛,一开始 \(1\)\(2\) 打,\(3\)\(4\) 打... 赢得队伍按照原来的相对顺序重新编号 \(1, 2, \ldots, 2^{n-1}\),按上规则进行比赛。给出一个胜率的概率矩阵,求最终最可能获胜的队伍。

数据范围:多组数据,\(n \leq 7\)

解题方法

\(f[i][j]\) 表示进行 \(i\) 场比赛,\(j\) 获胜的概率,可以得到转移方程:

\[f[i][j] = \sum f[i - 1][j]\times f[i-1][t]\times p[j][t] \]

其中 \(t\) 是在第 \(i\) 场比赛中可能与 \(j\) 交手的队伍,现在问题关键在于如何求解 \(t\)

一开始的队伍 \(1, 2, \ldots, 2^n\) 可以被分为 \(2^{n-1}\) 组,每个队可能和一个队打,容易发现第 \(i\) 场时 \(j\) 的对手可能有 \(2^{i-1}\) 个,整个 \(1,2,\ldots,2^n\) 可以被分为 \(2^{n-i}\) 组。所以只要知道当前 \(j\) 在第几组即可,可以知道在第

\[\frac{j-1}{2^{i-1}} + 1 \]

组。

代码

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 257;
double p[N][N], dp[N][N];
int n;

int main() {
    while(true) {
        scanf("%d", &n);
        if(n < 0) break;
        for(int i = 1; i <= (1 << n); i++)
            for(int j = 1; j <= (1 << n); j++) scanf("%lf", &p[i][j]);
        memset(dp, 0, sizeof dp);
        for(int i = 1; i <= (1 << n); i++) dp[0][i] = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= (1 << n); j++) {
                int cur = (j - 1) / (1 << (i - 1));
                if(cur & 1) {
                    for(int t = (cur - 1) * (1 << (i - 1)) + 1; t <= cur * (1 << (i - 1)); t++) {
                        dp[i][j] += dp[i - 1][j] * dp[i - 1][t] * p[j][t];
                    }
                } else {
                    for(int t = (cur + 1) * (1 << (i - 1)) + 1; t <= (cur + 2) * (1 << (i - 1)); t++) {
                        dp[i][j] += dp[i - 1][j] * dp[i - 1][t] * p[j][t];
                    }
                }
            }
        }
        double pWin = dp[n][1]; int winner = 1;
        for(int i = 2; i <= (1 << n); i++) {
            if(pWin < dp[n][i]) pWin = dp[n][i], winner = i;
        }
        printf("%d\n", winner);
    }
    return 0;
}
posted @ 2020-08-20 21:59  iNx  阅读(149)  评论(0编辑  收藏  举报