LINK

2 n 2^n 2n支球队,进行 n n n场比赛

对于每场比赛,存活下来的队伍按照编号递增的顺序站成一排

第一支队伍和第二支打,第三支队伍和第四支队伍打…一此类推

n n n场比赛后只有一个队伍会取胜,你只需要输出最可能获得胜利的那支队伍即可。


定义 f [ i ] [ j ] f[i][j] f[i][j]表示第 i i i轮第 j j j个人存活下来的概率

j j j在这一轮可能和 x 1 , x 2 . . . x k x_1,x_2...x_k x1,x2...xk

f [ i ] [ j ] = 1 k ∗ ∑ l = 1 k f [ x l ] [ j − 1 ] f[i][j]=\frac{1}k{}*\sum\limits_{l=1}^{k}f[x_l][j-1] f[i][j]=k1l=1kf[xl][j1]

但是这一轮,第 j j j个人要和哪些队伍打…

                                     ↗     ↖
                                    *        *
                                ↗ ↖       ↗↖
                               *     *      *   *
                            ↗↖   ↗↖   ↗↖  ↗↖
                            0  1   2  3  4   5  6   7

i i i轮第 j j j个人决斗的目标

110 111

就是从下往上第 i + 1 i+1 i+1层的根节点另一侧的所有节点

所以我们只需要判断每个节点在左侧还是右侧即可

所以把节点编号左移 i − 1 i-1 i1位,那么第 i i i位的二进制 0 / 1 0/1 0/1代表左右侧

除了这一位,其他位都需要相等

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 1009;
int n;
double f[maxn][maxn],p[maxn][maxn];
int main()
{
	while( cin >> n&&n!=-1 )
	{
		int mx = (1<<n);
		for(int i=0;i<mx;i++)
		for(int j=0;j<mx;j++)
			cin >> p[i][j];
		for(int i=0;i<mx;i++)	f[0][i] = 1;
		
		for(int i=1;i<=n;i++)
		for(int j=0;j<mx;j++)
		{
			f[i][j] = 0;
			for(int k=0;k<mx;k++)
			{
				if( ((j>>(i-1))^1)==(k>>(i-1)) )
					f[i][j] += f[i-1][j]*f[i-1][k]*p[j][k];
			}
		}
		double maxx = f[n][0];
		int index = 0;
		for(int i=1;i<mx;i++)
			if( f[n][i]>maxx )	maxx = f[n][i],index = i;
		cout << index+1 << endl;
	}
}