POJ - 3071 Football (概率DP)

题目链接:点击打开链接

题意: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),在进行^运算!就可以判断是否相邻了。

这个说不太好说明,写一下就很明了了!
可以用一个二叉树来更直观的理解  (j>>(i-1))^1) 跟 (k>>(i-1)分别表示一个父节点的两个子节点, 当(j>>(i-1))^1) == (k>>(i-1)时

不明觉厉

看代码把:

#include 
#include 
#include 

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;	
}

你可能感兴趣的:(POJ - 3071 Football (概率DP))