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
",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
",win);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/tlnshuju/p/6952785.html