题目大意:有2^n个足球队,编号依次为1,2,3……2^n,按照递增的顺序每两个相邻的队伍之间进行比赛,赢的队伍进入下一轮,然后再按照同样的规则进行比赛,共进行n轮比赛,最后剩下的一个队伍即为冠军,求最可能成为冠军的队伍。输入为2^n行2^n列,第i 行第 j 列的值p[i][j] 表示第i个队伍打败第j个队伍的概率。
解题思路:据大神说,这是个简单的dp题,于是我就一直推啊推,历经两个小时终于推出状态转移方程了,交一发wrong掉了,后来发现是一个小细节处理失误了,嘎嘎 貌似这是第一次独立做出dp题啊,HAPPY啊~~
我想的比较绕,先说一下大神的思路吧 ,简单易懂。
用dp[i][j]表示第i轮比赛第j个队伍获胜的概率,则dp[0][i] = 1 , dp[i][j] += dp[i-1][j] * dp[i-1][k] * p[j][k]; k取决于i和j。最后找到dp[n][i]中最大值对应的队伍号即可。
假设有4支队伍,第一轮对决:0和1,2和3;第二轮:0和2或3, 1和2或3,2和1或0,3和1或0 ……(队伍号下标从0开始)
第一轮:
dp[1][0] = dp[0][0] * dp[0][1] * p[0][1];
dp[1][1] = dp[0][1] * dp[0][0] * p[1][0];
dp[1][2] = dp[0][2] * dp[0][3] * p[2][3];
dp[1][3] = dp[0][3] * dp[0][2] * p[3][2];
第二轮:
dp[2][0] = dp[1][0] * dp[1][2] * p[0][2] + dp[1][0] * dp[1][3] * p[0][3] ;
dp[2][1] = dp[1][1] * dp[1][2] * p[1][2] + dp[1][1] * dp[1][3] * p[1][3];
dp[2][2] = dp[1][2] * dp[1][0] * p[2][0] + dp[1][2] * dp[1][1] * p[2][1];
dp[2][3] = dp[1][3] * dp[1][0] * p[3][0] + dp[1][3] * dp[1][1] * p[3][1];
这儿实现有个小技巧 ,具体看代码:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#define N 1024
double dp[10][N], p[N][N];
int main()
{
int n, m;
while(~scanf("%d", &m))
{
if(m==-1) break;
n = 1 << m;
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < n; j ++)
{
scanf("%lf", &p[i][j]);
}
}
for(int i = 0; i < n; i ++) dp[0][i] = 1;
for(int i = 1; i <= m; i ++)
{
int temp = (1 << (i-1));
for(int j = 0; j < n; j ++)
{
int s = j / temp;
s ^= 1;
// printf("s= %d\n", s);
dp[i][j] = 0;
for(int k = s * temp; k < s * temp + temp; k ++)
{
dp[i][j] += dp[i-1][j] * dp[i-1][k] * p[j][k];
}
}
}
double ma = 0;
int pos = 0;
for(int i = 0; i < n; i ++)
{
if(ma < dp[m][i])
{
ma = dp[m][i];
pos = i;
}
//printf("%.4lf\n", dp[m][i]);
}
printf("%d\n",pos+1);
}
return 0;
}
我的想法其实和这个差不多的,不过实现起来比人家的蹩脚多了,我是用dp[i] 表示第i个队伍获胜的概率,每轮比赛后更新一下dp[i]的值,这里需要用到一个辅助数组tmp[],tmp[i] += dp[i] * dp[k] * p[i][k],dp[i] = tmp[i]; 具体代码实现如下(比较恶心,我都不知道自己是怎么想出来的):
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#define N 1024
double dp[N], p[N][N], tmp[N];
int main()
{
int n, m;
while(~scanf("%d", &m))
{
if(m==-1) break;
n = 1 << m;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
scanf("%lf", &p[i][j]);
}
}
for(int i = 1;i <= n; i ++)
{
if(i & 1)
dp[i] = p[i][i+1];
else dp[i] = p[i][i-1];
}
for(int z = 1; z < m; z ++)
{
int temp = 1 << z;
for(int i = 1; i <= n; i+=(1<<z))
{
int s = i + temp;
if((i % temp != 0) && ((i / temp) & 1) )
s = i - temp;
for(int k = i; k < i + (1<<z); k++)
{
tmp[k] = 0;
for(int j = s; j< s+(1<<z); j++)
{
tmp[k] += dp[k] * dp[j] * p[k][j];
}
}
}
for(int k = 1; k <=n; k++)
{
dp[k] = tmp[k];
}
}
double ma = 0;
int pos = 0;
for(int i = 1; i <= n; i ++)
if(ma < dp[i])
{
ma = dp[i];
pos = i;
}
printf("%d\n",pos);
}
return 0;
}