POJ 3071 Football 【概率dp】
题目链接:http://poj.org/problem?id=3071
题目大意:给出 2^n 个队伍,队伍之间 1-2,3-4,5-6,这样分别两两淘汰,之后又是邻接队伍进行淘汰赛。给出两两之间A打败B的可能性,问哪只队伍最终获胜的可能性最大,并输出队伍序号。
思路:
1.概率dp题,关键在于选好dp的“ 内涵 ”, 跟回合有关,并且需要领接的队伍才能进行比赛。所以dp[i][j]代表第i轮第j个队伍存活的概率,最终我们只需要遍历dp[n][j](0~2^n - 1), 得到存活率最大的队伍就是答案。
2.还要运用到小技巧,每个队伍编号 -1 ,使其可以用位运算 ^1来判断是否在该轮邻接。
代码里的注释很详细
1 #include<stdio.h> 2 #include<string.h> 3 #define mem(a, b) memset(a, b, sizeof(a)) 4 5 double p[150][150]; 6 double dp[10][150]; //代表第i轮第j个队伍存活的概率 7 8 int main() 9 { 10 int n; 11 while(scanf("%d", &n) != EOF) 12 { 13 if(n == -1) 14 break; 15 mem(dp, 0); //WA了一次在这里,忘了清0. 16 int limit = 1 << n;//队伍编号处理为 0 ~ limit - 1 17 for(int i = 0; i < limit; i ++)//比赛未开始前每个队伍存活概率都为1 18 dp[0][i] = 1.0; 19 for(int i = 0; i < limit; i ++) 20 { 21 for(int j = 0; j < limit; j ++) 22 { 23 scanf("%lf", &p[i][j]); 24 } 25 } 26 for(int i = 1; i <= n; i ++)//枚举第几轮 27 { 28 for(int j = 0; j < limit; j ++)//枚举获胜队伍 29 { 30 for(int k = 0; k < limit; k ++)//枚举失败队伍 31 { 32 if((j >> (i - 1) ^ 1) == k >> (i - 1) && j != k)//满足在同一轮会相遇,即邻接条件 33 { 34 dp[i][j] += dp[i - 1][j] * dp[i - 1][k] * p[j][k];//两支比赛队伍上一轮必须都存活,并且在该轮j赢了k 35 } 36 } 37 } 38 } 39 double maxx = -1.0; 40 int ans; 41 for(int i = 0; i < limit; i ++) 42 if(maxx < dp[n][i]) 43 { 44 maxx = dp[n][i]; 45 ans = i + 1; 46 } 47 printf("%d\n", ans); 48 } 49 return 0; 50 }