一.解题技巧:
期望题倒着推,概率正着推
经常和概率dp联系
二.例题:
**1.POJ 3744**2.POJ - 3071 Football
代码:
#include <cstdio>
#include <cstring>
using namespace std;
const int N=(1<<7)+5;
double p[N][N],win[N][10];//每个选手进入第i轮的概率
int main()
{
int n;
while(scanf("%d",&n),n!=-1)
{
int m=1<<n;
double maxn=0;
int num=1;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
scanf("%lf",&p[i][j]);
}
memset(win,0,sizeof(win));
for(int i=1;i<=m;i++)
win[i][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{//cout<<"j="<<j<<" : ";
int len=1<<i;
int s=j/len;
if(j%len==0)
s--;
s*=len;//cout<<s+1<<"->"<<s+len<<" ";
int l=(j>(s+len/2)?(s+1):(s+len/2+1));
int r=(j>s+len/2?s+len/2:s+len);
//cout<<"l= "<<l<<" r="<<r<<" ";
for(int k=l;k<=r;k++)//第i轮第j个选手需要比较的区间
{
if(k==j)
continue;
win[j][i]+=win[j][i-1]*win[k][i-1]*p[j][k];
}
//cout<<win[j][i]<<endl;
if(i==n&&win[j][i]>maxn)
{
maxn=win[j][i];
num=j;
}
}
}//cout<<maxn<<endl;
printf("%d
",num);
}
return 0;
}
3.Bag of mice CodeForces - 148D
代码:
【记忆化搜索】
#include <bits/stdc++.h>
using namespace std;
double dp[1005][1005];//到达每个状态时的概率
bool vis[1005][1005];//标记状态,避免重复搜索
double dfs(int w,int b)//每次的状态对应的都是公主取的时刻
{
if(w<=0)//没有白色:输
return 0;
if(b<=0)//没有黑色:赢
return 1;
if(vis[w][b])
return dp[w][b];
vis[w][b]=1;
double &res=dp[w][b];//下面w,b改变了
res=1.0*w/(w+b);//或者取白色
if(b>=2)//或者取黑色
{
double t=b*1.0/(w+b);//公主取黑色
b--;
t*=b*1.0/(w+b);//龙取黑色
b--;
res+=t*(w*1.0/(w+b)*dfs(w-1,b)+b*1.0/(w+b)*dfs(w,b-1));//白色跑或者黑色跑
}
return res;
}
int main()
{
int w,b;
scanf("%d%d",&w,&b);
printf("%.9f
",dfs(w,b));
return 0;
}
【概率dp】
对公主而言有两种选择:
1.取白球,获胜;
2.取黑球,取决于后面的情况;
当选择黑球,为使公主获胜,龙必然选择黑球,那么跳出的球就有两种可能:白或黑
白:(dp[i][j]=1.0*j/(i+j)*1.0*(j-1)/(i+j-1)*1.0*i/(i+j-2)*dp[i-1][j-2];)
黑:(dp[i][j]=1.0*j/(i+j)*1.0*(j-1)/(i+j-1)*1.0*(j-2)/(i+j-2)*dp[i][j-3];)
边界条件:
全为白球:(dp[i][0]=1;)
全为黑球:(dp[0][i]=0;)
#include <bits/stdc++.h>
using namespace std;
double dp[1005][1005];
int main()
{
int w,b;
scanf("%d%d",&w,&b);
for(int i=1;i<=w;i++)
dp[i][0]=1;
for(int i=1;i<=w;i++)
{
for(int j=1;j<=b;j++)
{
dp[i][j]=1.0*i/(i+j);//取白球
double t=1.0*j/(i+j)*1.0*(j-1)/(i+j-1);//两者都取黑球
if(j>=3)//跳白球
dp[i][j]+=t*1.0*(j-2)/(i+j-2)*dp[i][j-3];
if(i>=1&&j>=2)//跳黑球
dp[i][j]+=t*1.0*i/(i+j-2)*dp[i-1][j-2];
}
}
printf("%.9f
",dp[w][b]);
return 0;
}
4.Check the difficulty of problems POJ - 2151
分析:
(dp[i][j][k]):表示第 (i) 个队在前 (j) 题中做对 (k) 道的概率;(记住这种表示方法)
状态转移方程:(dp[i][j][k]=dp[i][j-1][k-1]*p[i][j]+dp[i][j-1][k]*(1-p[i][j]));
边界条件:(dp[i][0][0]=1,dp[i][j][0]=prod_{k=1}^{j}{(1-p[i][k])})
(s[i][j]):表示第 (i) 个队整场比赛中做出的题目数 (leq j) 的概率;
(s[i][j]=sum_{k=0}^{j}{dp[i][m][k]})
令 (P_1)表示每队至少做出一题的概率,(P_2)表示每队做出的题目数为 ([1,n-1]) 的概率;
(P_1=prod_{i=1}^{t}{(1-s[i][0])})
(P_2=prod_{i=1}^{t}{(s[i][n-1]-s[i][0])})
最终结果为:(P_1-P_2),即所有队都至少做出一道题且至少有一个队做出了 (n) 道题。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1005;
double dp[N][32][32],s[N][32],p[N][32];
int main()
{
int m,t,n;
while(scanf("%d%d%d",&m,&t,&n),m||t||n)
{
for(int i=1;i<=t;i++)
for(int j=1;j<=m;j++)
scanf("%lf",&p[i][j]);
for(int i=1;i<=t;i++)
{
dp[i][0][0]=1.0;
for(int j=1;j<=m;j++)
{
dp[i][j][0]=dp[i][j-1][0]*(1-p[i][j]);
for(int k=1;k<=j;k++)
dp[i][j][k]=dp[i][j-1][k-1]*p[i][j]+dp[i][j-1][k]*(1-p[i][j]);
}
}
for(int i=1;i<=t;i++)
{
for(int j=0;j<=m;j++)
{
s[i][j]=0;
for(int k=0;k<=j;k++)
s[i][j]+=dp[i][m][k];
}
}
double p1=1.0,p2=1;
for(int i=1;i<=t;i++)
{
p1*=(1-s[i][0]);
p2*=(s[i][n-1]-s[i][0]);
}
printf("%.3f
",p1-p2);
}
return 0;
}