POJ 3071 Football 概率dp
题目链接:
http://poj.org/problem?id=3071
Football
Memory Limit: 65536K
输入
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.
输出
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.
样例输入
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
样例输出
2
题意
有2^n支队伍打比赛,每次编号靠近的两个人(1,2;3,4;...)要打场比赛,也就是每轮要淘汰掉一半的选手,直到剩下一支队伍。
现在你已经知道对于任意的两支队伍i,j,i打败j的概率是p,j打败i的概率是1-p。问最后获胜概率最大的那支队伍的编号。
题解
这里以比赛的轮次作为阶段很自然,所以我们可以想到状态:dp[i][j]表示第i轮j胜出的概率,则有状态转移:
dp[i][j]=sigma(dp[i-1][j]*dp[i-1][k]*pro[j][k])
,其中k表示第i轮有机会和j交手的队伍,pro[j][k]表示j打赢k的概率。
代码
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf
typedef __int64 LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;
const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-9;
const double PI = acos(-1.0);
//start----------------------------------------------------------------------
const int maxn=8;
double dp[maxn][1<<maxn];
double G[1<<maxn][1<<maxn];
int n;
int main() {
while(scf("%d",&n)==1&&n!=-1){
rep(i,0,(1<<n)){
rep(j,0,(1<<n)){
scf("%lf",&G[i][j]);
}
}
clr(dp,0);
rep(i,0,1<<n) dp[0][i]=1.0;
for(int i=1;i<=n;i++){
int mask=(1<<n)-(1<<i);
rep(j,0,(1<<n)){
rep(k,0,(1<<n)){
if(k==j) continue;
//这个两个条件筛出第i轮有机会和j打的人
if((j&mask)!=(k&mask)) continue;
if(((j&(1<<(i-1)))^(k&(1<<(i-1))))==0) continue;
dp[i][j]+=dp[i-1][j]*dp[i-1][k]*G[j][k];
}
}
}
double ma=0.0;
int pos=-1;
rep(i,0,(1<<n)){
if(ma<dp[n][i]){
ma=dp[n][i];
pos=i;
}
}
prf("%d\n",pos+1);
}
return 0;
}
//end-----------------------------------------------------------------------