POJ 3071 Football [概率DP]

Description

Consider a single-elimination football tournament involving 2n teams, denoted 1, 2, …, 2n. In each round of the tournament, all teams still in the tournament are placed in a list in order of increasing index. Then, the first team in the list plays the second team, the third team plays the fourth team, etc. The winners of these matches advance to the next round, and the losers are eliminated. After n rounds, only one team remains undefeated; this team is declared the winner.

Given a matrix P = [pij] such that pij is the probability that team i will beat team j in a match determine which team is most likely to win the tournament.


题意:

给出1<<n只队伍,他们两两之间开始比赛,赢的作为新的1<<(n-1)只队伍,继续两两比赛,其中队伍I打败队伍J的概率为Pij,问最后赢的概率最大的队伍是那一支(从1编号到1<<N)

范围:

N<=7

解法:

首先分析做法,发现其结构是一棵满二叉树,受到线段树维护信息的启发,可以考虑每一轮结束后,I队继续比赛下去的概率,可以用DP[I][J]表示J轮后,I获胜的概率,J至多为7。

转移时只需要枚举另一队伍K,计算I继续获胜的概率

DP[I][J]=DP[I][J-1]*ΣDP[K][J-1]*P[I][K]

判断K是否会和I在恰好J轮后相遇,可以用位操作进行判断,如果学过ZKW线段树,可以很容易写出条件:

if(((i+m-1)>>j)==((k+m-1)>>j)&&((i+m-1)>>(j-1))!=((k+m-1)>>(j-1)))

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;


int n,m;


double dp[129][8];
double p[129][129];
int main(){
    while(scanf("%d",&n)!=EOF){
        if(n==-1)break;
        m=1<<n;
        rep(i,1,m)
        rep(j,1,m){
            scanf("%lf",&p[i][j]);
        }
        mem(dp,0);
        rep(i,1,m)dp[i][0]=1.0;
        rep(j,1,n){
            rep(i,1,m){
                rep(k,1,m){
                    if(((i+m-1)>>j)==((k+m-1)>>j)&&((i+m-1)>>(j-1))!=((k+m-1)>>(j-1))){
                        //如果I和K在第J轮可能进行比赛
                        dp[i][j]+=dp[i][j-1]*dp[k][j-1]*p[i][k];
                    }
                }
            }
        }
        double maxx=-1;
        int ans=-1;
        rep(i,1,m){
            if(dp[i][n]>maxx){
                maxx=dp[i][n];
                ans=i;
            }
        }
        printf("%d\n",ans);
    }
    return 0;

}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值