题目链接:点击打开链接
题意:2的n次方个球队要比赛,求夺冠概率最大的战队。
比赛流程如下:
1 2 3 4
|________| |________| 第一轮
|_________________| 第二轮
| 第三轮
冠军
题解:先解释一下样例把。如假如第1轮1号战队获胜了,那么他将面临的战队为3号战队和4号战队
那么第二轮 1号战队胜出的概率为 P(第一轮1号打败2号的概率)* P(第一轮3号打败4号的概率)* P(第二轮1打败3的概率)+ P(第一轮1号打败2号的概率)* P(第一轮4号打败3号的概率)* P(第二轮1打败4的概率).
知道如何算每一轮某只战队获胜的概率之后
我们可以设DP【i】【j】表示为第i轮 j 战队获胜的概率。
显然我们可以知道
DP【i】【j】= Σ(DP【i-1】【j】 * DP【i-1】【k】* DP【j】【K】) K的取值为第 i 轮 j 战队可能遇上的所有战队。
转移方程好推,这道题的难点在于,你如何知道第 i 轮 j 战队会和那些战队打。
反正我是没模拟出来,只能去看大佬们的操作。
j和k 都右移(i-1)位,然后判断k,是奇数还是偶数,奇数的话就 把结果-- ,反之++。
再比较是否相等=-=。
骚操作不知道原因只知道这么操作可以判断成功。
dalao的解释如下
用到了^运算符,有一个性质 (2n) ^ (1) = 2n+1; (2n+1) ^ (1) = 2n
因此先给每一个数 >> (i-1),在进行^运算!就可以判断是否相邻了。
不明觉厉
看代码把:
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std ;
const int maxn = 105;
double dp[maxn][maxn];
double a[maxn][maxn];
int main(){
int n;
while(scanf("%d",&n)){
if(n == -1) break;
int m = 1 << n;
for(int i = 1 ; i <= m ; i ++){
for(int j = 1 ; j<= m ;j ++ ){
scanf("%lf",&a[i][j]);
}
}
memset(dp,0,sizeof(dp));
for(int i = 1 ; i <= m ; i ++){
dp[0][i] = 1;
}
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j ++){
for(int k = 1 ; k <= m ; k ++){
int p = (j-1) >> (i-1);
int q = (k-1) >> (i-1);
if(q%2 == 0){
q ++ ;
if(p == q){
dp[i][j] += dp[i-1][j] * dp[i-1][k] * a[j][k];
}
}
else {
q --;
if(p == q){
dp[i][j] += dp[i-1][j] * dp[i-1][k] * a[j][k];
}
}
}
}
}
int ans = 0 ;
for(int i = 1 ; i <= m ; i ++){
//cout << dp[n][i] << " ";
if(dp[n][ans] < dp[n][i])
ans = i;
}
cout << ans << endl;
}
return 0;
}