题意:有\(2^n\)个人要进行比赛,每次\(2i\)与\(2i+1\)号人进行比赛.这一轮中赢的人进入下一轮.下一轮比赛的时候把进入这一轮的人按编号排好,仍然是像之前那样相邻的进行一次比赛.最后只剩下一个人.数据给出对于$ x,y\(,\)x\(打赢\)y\(的概率.第\)i$轮比赛会角逐出\(2^{n-i}\)个赢家.我们要在比赛开始前,猜每轮的赢家(一轮的赢家一定要是上一轮的赢家).第$ i $轮每猜中一个赢家就会得到\(2^{i-1}\)的得分.求最大的期望得分.\(n<=6\)
分析:不难发现整个比赛其实是一棵完全二叉树,树的第i层代表第i轮比赛,树上的每个节点都代表一个区间(这一轮的参赛人的编号).
所以我们考虑在树上dfs,设\(w[i][j]\)表示在以i号节点为根的树中(即在第i轮比赛中)j获胜的概率.设\(f[i][j]\)表示在以i号节点为根的树中(即在第i轮比赛中)j获胜的最大期望得分.
对于w数组的转移,我们只要从左子树和右子树中分别选一个节点,然后结合概率来算就好了.对于f数组的转移,一定要在w数组转移之后,同样是从左子树和右子树中分别选一个节点,然后算期望就好了.具体的转移方程直接看代码吧,很好理解.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=105;
double p[N][N],w[N*10][N],f[N*10][N];
inline void dfs(int root,int l,int r,int val){
if(l==r){//叶子节点,即最后一轮比赛
w[root][l]=1;
f[root][l]=val;
return;
}
int mid=(l+r)>>1;
dfs(root<<1,l,mid,val/2);
dfs(root<<1|1,mid+1,r,val/2);
for(int i=l;i<=mid;++i)
for(int j=mid+1;j<=r;++j){
w[root][i]+=w[root<<1][i]*w[root<<1|1][j]*p[i][j];
}
for(int i=mid+1;i<=r;++i)
for(int j=l;j<=mid;++j){
w[root][i]+=w[root<<1][j]*w[root<<1|1][i]*p[i][j];
}
for(int i=l;i<=mid;++i)
for(int j=mid+1;j<=r;++j){
f[root][i]=max(f[root][i],w[root][i]*val+f[root<<1][i]+f[root<<1|1][j]);
}
for(int i=mid+1;i<=r;++i)
for(int j=l;j<=mid;++j){
f[root][i]=max(f[root][i],w[root][i]*val+f[root<<1][j]+f[root<<1|1][i]);
}
}
int main(){
int n=read();n=1<<n;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
int x=read();
p[i][j]=0.01*x;//把整数变为概率
}
}
dfs(1,1,n,n/2);double ans=0.0;
for(int i=1;i<=n;++i)ans=max(ans,f[1][i]);
printf("%.10lf\n",ans);
return 0;
}