您的位置:首页 > 其它

POJ 3071 Football (很好的一个概率DP)

2015-07-12 00:05 323 查看
题意:1...2^n个队伍比赛,每一轮所有的队伍按照顺序两两比赛。比如说第一轮就是1对2,3对4。给出一个矩阵P= [pij] ,pij表示i队打败j队的概率。求最后哪个队伍最有可能拿冠军。

思路:设dp[i][j] 为在第i轮中j胜出的概率。

那么dp[i][j] = sigma(dp[i-1][k]*p[j][k]) k是在第i轮中所有有可能与j对战的队伍。

问题的关键是怎么找出k。

看见这个区间的划分我想到了线段树。线段树的每个节点正好可以划分每一轮对应的区间。

设某个节点对应的区间为[l,r]。通过区间的范围可以算出对应的轮数。

那么mid = (l + r) >> 1;

[l,mid]区间内的每个队伍都会和[mid+1,r]的每个队伍比赛。所以两层for循环解决。

我的代码:

#include<cstdio>
#include<iostream>
#include<cstring>

using namespace std;
const int maxn = 205;

int n;
double p[maxn][maxn];
double dp[maxn][maxn];

int mpow(int a,int n){
int res = 1;
while(n){
if(n & 1) res *= a;
a *= a;
n >>= 1;
}
return res;
}

void build(int l,int r){
if(r - l == 1){
dp[1][l] = p[l][r];
dp[1][r] = p[r][l];
return;
}
int mid = (l + r) >> 1;
build(l,mid);
build(mid+1,r);
int m = 0;
while((r - l) != (1 << m) - 1) m++;
for(int i = l ;i <= mid ; i++){
for(int j = mid + 1; j <= r ; j++){
dp[m][i] += dp[m-1][i]*dp[m-1][j]*p[i][j];
dp[m][j] += dp[m-1][j]*dp[m-1][i]*p[j][i];
}
}
}

int main(){
while(scanf("%d",&n)){
if(n == -1) break;
int k = n;
n = mpow(2,n);
memset(dp,0,sizeof(dp));
for(int i = 1; i <= n ; i++){
for(int j = 1; j <= n ; j++){
scanf("%lf",&p[i][j]);
}
}
build(1,n);
double tmp = 0;
int res = 0;
for(int i=1;i<=n;i++){
if(dp[k][i] > tmp){
tmp = dp[k][i];
//cout<<tmp<<endl;
res = i;
}
}
printf("%d\n",res);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: