思路
我们采用动态规划的思想,从每一轮出发,计算每支队伍这轮获胜的概率。
基于全概率公式,队伍概论胜利的概率可以由对阵其他可以打的队伍获胜的概率之和。
现在问题就是如何表示该轮可对阵的队伍:
假设由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; }