思路

我们采用动态规划的思想,从每一轮出发,计算每支队伍这轮获胜的概率。
基于全概率公式,队伍概论胜利的概率可以由对阵其他可以打的队伍获胜的概率之和。
现在问题就是如何表示该轮可对阵的队伍:
假设由2^n只球队,如果j在[0,2^(n-1)]里面,k在[2^(n-1)+1,2^n]里面,那么很明显他们要在最后一轮才能对决。
同理,我们缩小范围,如果将2^n对折i次后,j和k仍在同一区间,则说明在n-i轮他们可以对决(也只有那一轮他们可以对决)
于是我们得出下面美观的代码和令人头疼的位运算。(代码参考林厚从的数学一本通)

代码

#include<stdio.h>
#include<cstring>
using namespace std;

double dp[10][150];//dp[i][j]表示第i场比赛中j胜出的概率
double p[150][150];  //p[i][j]存储i对战j胜出的概率 

signed main(){
    int n,team; 
    while(scanf("%d",&n)!=EOF){
        if(n==-1) break;
        team=(1<<n);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<team;i++){
            for(int j=0;j<team;j++){
                scanf("%lf",&p[i][j]); 
            }
        }
        for(int i=0;i<team;i++) dp[0][i]=1;
        for(int i=1;i<=n;i++){ //2^n个人要进行n场比赛
            for(int j=0;j<team;j++){ //j是要获胜的那支队 
                int t=j/(1<<(i-1));
                t^=1; //用来判断朝左块打还是右块打 
                dp[i][j]=0; //初始值为0 
                for(int k=t*(1<<(i-1));k<t*(1<<(i-1))+(1<<(i-1));k++)  
                //枚举当前能打的队伍 
                    dp[i][j]+=dp[i-1][j]*dp[i-1][k]*p[j][k];
                    //计算j在i轮中获胜的概率+=j在i-1轮获胜*k在i-1轮获胜*j赢k 
             } 
        }
        int ans;
        double tmp=0; 
        for(int i=0;i<team;i++){
            if(dp[n][i]>tmp){
                ans=i; //记录在n轮后获胜概率最高的队伍 
                tmp=dp[n][i];
            }
        }
        printf("%d\n",ans+1);
    }
    return 0;
}