记录一下遇到的各种题目:
直接求:
买饮料,假设有n个不同球星的名字,每个名字出现的概率相同,平均需要买几瓶饮料才能凑齐所有的名字呢?
分析:
假设现在已经有k个球星的名字,那么要使球星的名字达到k + 1个平均需要买多少饮料:
当有k个人的时候,抽到下一个的概率是n-k/n 所以平均需要n/n-k瓶才能买到下一个,所以:ans = n(1/1+1/2+…+1/n)直接计算即可
动态规划
由于期望的线性性质,我们可以在期望和概率之间建立一定的递推关系,可以通过动态规划解决一些概率问题
概率和期望的最值问题往往使用动态规划的方法解决
一般求概率市政推,求期望是逆推
dp转移方程(组)中有环的多半用高斯消元(手动,写代码)
树上dp转移,dfs对方程迭代解
当公式或计算时有除法时,注意分母是否为零
2.
2^n个球队,相邻的进行比赛,告诉i球队胜过j球队的概率(矩阵),问最后获胜的最大概率的球队?
分析
概率dp,用位运算判断是否相邻
^运算符的一个性质: (2n) ^ (1) = 2n+1; (2n+1) ^ (1) = 2n,,我们如果先>>(i - 1) , 再进行 ^ 运算即可
dp[i][j]表示第i轮j球队获胜的概率,遍历 k,k表示 j 可以战胜的球队
#include<bits/stdc++.h>
using namespace std;
double p[200][200],dp[200][200];
int num[10];
void init(){
num[0] = 1;
for(int i = 1; i < 8; i ++)
num[i] = 2 * num[i - 1];
}//可以直接用1 << n 代替
int main(){
init();
int n;
while(~scanf("%d", &n) && n != -1){
for(int i = 1; i <= num[n]; i++){
for(int j = 1; j <= num[n]; j ++){
scanf("%lf", &p[i][j]);//数据类型出错
}
}
memset(dp, 0, sizeof(dp));//没有写这一句
for (int i = 1; i <= num[n]; ++i)dp[0][i] = 1.0;//忘记写这一局了
for(int i = 1; i <= n; i++)
for(int j = 1; j <= num[n]; j++)
for(int k = 1; k <= num[n]; k++){
if ((((j-1) >> (i-1)) ^ 1) == ((k-1) >> (i-1)))
dp[i][j] += dp[i - 1][j] * dp[i - 1][k] * p[j][k];
}
int ans;
double p = 0;
for(int i = 1; i <= num[n]; i++){
if(dp[n][i] > p){
ans = i;
p = dp[n][i];
}
}
printf("%d\n", ans);
}
return 0;
}
3、
很长的路上有n个矿井,走一步的概率为p, 走两步的概率为1 - p,问安全通过的可能性
分析:
走到第 i 位置的概率dp[i] = p * dp[i - 1] + (1 - p) * dp[i - 2]; 虽然n不大,但是矿井可能很远(10^8 , 连O(logn)都不可以),所以像这种递推公式,我们可以用矩阵快速幂优化;转移矩阵为
p 1-p
1 0
安全通过矿井,实际上就是安全通过每个矿井,是相互独立的(并不是从第一个位置开始算的概率是相互独立的,想想就知道不独立,他的独立是将,我们将道路进行分段,在每段上的安全通过矿井的概率是独立的),所以我们只需计算出通过每个矿井的概率,然后相乘即可,将舌头过第一个矿井,即:我们现在处在的位置上已经是 x[1] + 1 位置上了,对于第i 位置上和第j 位置上有雷的情况,安全的位置即 j - i - 1;;
迭代
动态规划要求问题无后效性,如果问题不可避免的有后效性,无门可以采用迭代的方法进行计算
但他要求问题有收敛性并且瘦脸的速度足够快