POJ 3071 Football(概率DP)

Football
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 3729 Accepted: 1905

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.

Input

The input test file will contain multiple test cases. Each test case will begin with a single line containing n (1 ≤ n ≤ 7). The next 2n lines each contain 2n values; here, the jth value on the ith line represents pij. The matrix P will satisfy the constraints that pij = 1.0 − pji for all i ≠ j, and pii = 0.0 for all i. The end-of-file is denoted by a single line containing the number −1. Note that each of the matrix entries in this problem is given as a floating-point value. To avoid precision problems, make sure that you use either the double data type instead of float.

Output

The output file should contain a single line for each test case indicating the number of the team most likely to win. To prevent floating-point precision issues, it is guaranteed that the difference in win probability for the top two teams will be at least 0.01.

Sample Input

2
0.0 0.1 0.2 0.3
0.9 0.0 0.4 0.5
0.8 0.6 0.0 0.6
0.7 0.5 0.4 0.0
-1

Sample Output

2

Hint

In the test case above, teams 1 and 2 and teams 3 and 4 play against each other in the first round; the winners of each match then play to determine the winner of the tournament. The probability that team 2 wins the tournament in this case is:

P(2 wins) P(2 beats 1)P(3 beats 4)P(2 beats 3) + P(2 beats 1)P(4 beats 3)P(2 beats 4)
p21p34p23 + p21p43p24
= 0.9 · 0.6 · 0.4 + 0.9 · 0.4 · 0.5 = 0.396.

The next most likely team to win is team 3, with a 0.372 probability of winning the tournament.


编号分别为1、2、3……2^n的2^n个队伍参加比赛,每一轮相邻的两两比赛,胜者晋级下一轮,负者淘汰,直到只剩下一支队伍。

基本思路:dp[i][j]表示第i轮比赛j号队胜利的概率,第i轮j要获胜,首先第(i-1)轮j要获胜,(所以dp[0][j]要初始化为1),用k表示能与j在第i轮比赛中相遇的对手,而且如果j与k要相遇,k也必须在第(i-1)轮中获胜,所以状态转移方程为 dp[i][j]+=dp[i-1][j]*dp[i-1][k]*p[j][k],注意,是相加,即把遇到每个对手且获胜相加就是j在这一轮获胜的总概率。

状态转移方程得到了,下一个点就是找到第i轮j可能遇到的对手。有很多种方法,比如第一份代码就是通过标记记录j与k是否在第i轮之前交手,如果vis[j][k]=1,那么他们必定有一个被淘汰,就不可能在第i轮相遇,(k的范围的获得详见代码);第二份代码判断的方式就比较简单了,通过位来进行判断,自己可以带几组数据试一试,规律基本就能发现了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=(1<<7)+5;//注意括号
double p[MAXN][MAXN],dp[10][MAXN];
int vis[MAXN][MAXN];
int n;

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
    while(scanf("%d",&n)!=EOF&&n!=-1)
    {
        int people=1<<n;
        for(int i=0;i<people;i++)
            for(int j=0;j<people;j++)
                scanf("%lf",&p[i][j]);
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<people;i++)
        {
            dp[0][i]=1;
            vis[i][i]=1;
        }
        for(int i=1;i<=n;i++)
            for(int j=0;j<people;j++)
            {
                if(i==1)
                {
                    dp[i][j]=p[j][j/2*4+1-j];
                    vis[j][j/2*4+1-j]=1;
                }
                else
                {
                    int s=j/(1<<i)*(1<<i),e=j/(1<<i)*(1<<i)+(1<<i);//不难发现第i轮可能与j交手的人一共有(1<<i)个(包含j本身)
                                                                   //因此可以在第i轮时把队伍按照(1<<i)个一组进行分组,接下来就只要找到j的分组
                                                                   //所以s=j/(1<<i)*(1<<i)可以求出可能与j交手的人的最小编号
                                                                   //e=j/(1<<i)*(1<<i)+(1<<i)是最大编号
                    for(int k=s;k<e;k++)
                        if(!vis[j][k])//如果没有交过手
                        {
                            vis[j][k]=1;
                            dp[i][j]+=dp[i-1][j]*dp[i-1][k]*p[j][k];
                        }
                }
            }
        double maxx=-1;
        int win=0;
        for(int i=0;i<people;i++)
        {
            if(dp[n][i]>maxx)
            {
                maxx=dp[n][i];
                win=i+1;
            }
        }
        printf("%d\n",win);
    }
    return 0;
}


#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma commment(linker,"/STACK: 102400000 102400000")
#define lson a,b,l,mid,cur<<1
#define rson a,b,mid+1,r,cur<<1|1
using namespace std;
const double eps=1e-6;
const int MAXN=(1<<7)+5;

double p[MAXN][MAXN],dp[10][MAXN];
int n;

int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
    while(scanf("%d",&n)&&n!=-1)
    {
        int people=1<<n;
        for(int i=0;i<people;i++)
            for(int j=0;j<people;j++)
                scanf("%lf",&p[i][j]);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<people;i++)
            dp[0][i]=1;
        for(int i=1;i<=n;i++)
            for(int j=0;j<people;j++)
                for(int k=0;k<people;k++)
                    if(((j>>(i-1))^1)==(k>>(i-1)))//通过二进制来判断在第i轮是否能相遇
                        dp[i][j]+=dp[i-1][j]*dp[i-1][k]*p[j][k];
        double maxx=-1;
        int win=0;
        for(int i=0;i<people;i++)
        {
            //printf("%.3lf ",dp[n][i]);
            if(dp[n][i]>maxx)
            {
                maxx=dp[n][i];
                win=i+1;
            }
        }
        printf("%d\n",win);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值