POJ 3071 Football (概率DP)

题目类型  概率DP

题目意思
给出  2^n 支足球队 (1 <= n <= 7) 足球队已经排列好顺序 假设分别标记为 0, 1, 2, 3, 4, 5, 6, 7
那么每轮对于没有淘汰的足球队 每次从左往右选两支足球队进行比赛 例如第一轮的比赛的对阵情况肯定是这样的->  (0,1) (2,3) (4,5) (6, 7)
然后第二轮的对阵情况要根据第一轮的结果再进行配对 例如第一轮的胜者分别是 0 2 5 7的话第二轮的对阵情况就是 (0,2) (5,7)
 依次这样最终决出冠军 现在已经知道了任意一支队伍战胜任意另一支队伍的概率 问最有希望获得冠军的队伍是哪一支

解题方法
DP
dp[i][j]  -> 第 i 支队伍打完了第 j 轮比赛的胜利概率
那么显然有 dp[i][j] += dp[i][j-1] * dp[i的对手][j-1] * Win[i][i的对手]; (Win[i][j]表示i战胜j的概率) 所以关键在于枚举 i 的对手
可以以二进制的特性来考虑  例如 9 的二进制表示是 1001 (从高位往低位看 当这一位为0说明这支队伍属于左边的部分 为1属于右边的部分)
因此 9 如果一直胜利的话它第3轮的可能对手就是 0000->0111 第2轮的可能对手是 1100->1111 第1轮的可能对手是 1010->1011 第0轮的可能对手是
1000->1000 (轮数从0开始)
即由队伍的序号可以直接求出它在某一轮的可能对手 例如第 i 轮(i从0开始) 那么二进制下这支球队与它的对手只有倒数(i+1)位是不同的, 假设前面的位对应的权值是 tj 那么如果这支球队位于左半部分 则它的对手的取值范围是 tj + (1<<i) -> tj + (1<<i) + (1<<i)-1 如果位于右半部分则它的对手的取值范围是tj + 0 -> tj + (1<<i)-1, 例如计算队伍9的第1轮的对手(从第0轮开始) 那么大家前面有2位是相同的 即10
接下来分叉了9去到了左半边 那么右半边就是它的对手 1000 + (1<<1) -> 1000+(1<<1)+(1<<1)-1 即十进制下的 10->11
                                 |
               |——————————|
        |————|                  |————|
 |——|        |——|        |——|        |——|
0 1   2  3    4  5   6  7    8 9   10 11 12 13  14 15

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = (1<<7);
double  dp[maxn][10];
int n;
double  M[maxn][maxn];

int main() {
	freopen("in", "r", stdin);
	while(scanf("%d",  &n), n != -1) {
		int m = (1<<n);
		for( int i=0; i<m; i++ ) {
			for( int j=0; j<m; j++ ) {
				scanf("%lf", &M[i][j]);
			}
		}
		memset(dp, 0, sizeof(dp));
		for( int i=0; i<n; i++ ) {
			for( int j=0; j<m; j++ ) {
				int tj = j;
				tj >>= (i+1);
				tj <<= (i+1); // 首先把左边其同部分取出来
				if((j & (1<<i))==0) { // 如果位于左边部分那么对手的区间就是 tj+(1<<i)+0 -> tj+(1<<i)+(1<<i)-1 
					for( int k=0; k<(1<<i); k++ ) {
						dp[j][i] += (i==0?1:dp[j][i-1]) * (i==0?1:dp[tj+(1<<i)+k][i-1]) * M[j][tj+(1<<i)+k];
					}
				}
				else { // 如果位于右边部分那么对手区间就是 tj+0 -> tj+(1<<i)-1
					for( int k=0; k<(1<<i); k++ ) {
						dp[j][i] += (i==0?1:dp[j][i-1]) * (i==0?1:dp[tj+k][i-1]) * M[j][tj+k];
					}
				}
			}
		}
		double ans = 0;
		int res;
		for( int i=0; i<m; i++ ) {
			if(ans < dp[i][n-1]) {
				ans = dp[i][n-1];
				res = i+1;
			}
		}
		printf("%d\n", res);
	}
	return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值