概率论应用
概率论应用(概率期望dp)
基础知识
条件概率公式
在事件B已经发生的条件下,A发生的概率 \(P(A|B) = \frac{P(AB)}{P(B)}\)
全概率公式
\(P(B) = P(BΩ) = P[B(\bigcup_{i=1}^{n}A_i)] = P(\bigcup_{i=1}^{n}A_i B) = \sum_{i=1}^{n}P(A_iB)=\sum_{i=1}^{n}P(A_i)P(B|A_i)\)
贝叶斯公式
\(P(A_i | B) = \frac{P(A_iB)}{P(B)} = \frac{P(A_i)P(B|A_i)}{P(B)}\)
由\(P(B) = \sum_{i=1}^{n}P(A_i)P(B|A_i)\)得
\(P(A_i | B) = \frac{P(A_i)P(B|A_i)}{\sum_{i=1}^{n}P(A_i)P(B|A_i)}\)
期望
- \(期望的线性性质 随机变量X,Y E(X+Y) = E(X) + E(Y)\)
- 把所有情况不重复的,不遗漏的分成若干类,每类计算期望,按每类的概率加权求和
LightOj 1027
题意: 有n扇门,每次你可以选择其中一扇。xi为负值的门带你abs(xi)后又回到原点。xi为正值
的门则带你离开迷宫。每次你都没有经验没有记忆。选择每扇门的概率相等。求走出迷宫
的时间期望值。
思路:
解法一: 假设走出迷宫的期望值为E,
选择到正数的门,走出的期望值为 $T1 \frac{1}{n} $
选择到负数的门,走回到起点(起点走出的期望为E),所以走出的期望值为 \((T2 + E) \frac{1}{n}\)
于是\(E =T1 \frac{1}{n}+(T2 + E) \frac{1}{n}\)
化简一下 $E = \frac{\sum{T_i}}{cnt正} $
解法二:
走出的时间期望值E = 走出去次数的期望 × 平均时间
假设正的门有\(k\)个,则走出的概率为\(\frac{k}{n}\),于是走出去的次数的期望为\(\frac{n} {k}\)(列式子,错位相减,求极限即可得出)
平均时间 = \(\frac{\sum{T_i}}{n}\)
E = \(\frac{n} {k}\frac{\sum{T_i}}{n} = \frac{\sum{T_i}}{k}\)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int gcd(int x,int y){
return y == 0?x:gcd(y,x%y);
}
int main()
{
int T;
int cas = 1;
cin>>T;
while(T--){
int n;
int x, total = 0, cnt = 0;
scanf("%d",&n);
for(int i = 0;i <n;i++){
scanf("%d",&x);
if(x < 0) total += -x;
else total += x,cnt++;
}
int g = gcd(cnt, total);
printf("Case %d: ",cas++);
if(!cnt) printf("inf\n");
else printf("%d/%d\n",total / g, cnt / g);
}
return 0;
}
Light 1030
题意:给定n个格子以及每个格子上的gold值,你从第一个格子出发,每次掷1-6的骰子,根据掷出的值前进。若当前位置 + 掷出的值 > n,则重新掷骰子,到达第n个格子游戏结束。问你获得gold值的期望。
思路: 由于每个格子是相互独立的,分别计算就好了,
定义dp[i]为到达格子i的概率,则期望值就是\(\sum_{i=1}^{n}{dp[i]a[i]}\)
#include<bits/stdc++.h>
#define LL long long
#define db long double
using namespace std;
const int N = 1e2 + 10;
double dp[N];
int main()
{
int T;
int cas = 1;
cin>>T;
while(T--){
int n;
scanf("%d",&n);
memset(dp, 0, sizeof(dp));
double ans = 0;
for(int i = 1;i <= n;i++){
int x;
scanf("%d",&x);
if(i == 1) dp[i] = 1;
else{
int p = max(1,i - 6);
for(int j = p;j < i;j++) dp[i] += dp[j] / min(6,n - j);
}
ans += x * dp[i];
}
printf("Case %d: ",cas++);
printf("%.12lf\n",ans);
}
return 0;
}
Light Oj 1038
题意:给一个数D,每次可以变成它的因子,求变成1的期望次数 \(1<=D<=10^5\)
思路: \(设dp[x]为x变成1的期望次数, x的因子个数为cnt,则dp[x] = \frac{dp[x] + \sum_{d|x,d!=x}{dp[d]} + cnt}{cnt}\)
解得 $ dp[x]= \frac{\sum_{d|x,d!=x}{dp[d]} + cnt}{cnt-1}$
\(预处理dp数组即可,复杂度nlogn\)
const int N = 1e5 + 10;
double dp[N];
void init(){
dp[1] = 0;
for(int i = 2;i < N;i++){
int cnt = 0;
dp[i] = 0;
for(int j = 1;j * j <= i;j++){
if(i % j == 0){
cnt++;
if(i / j != j) cnt++;
}
}
for(int j = 1;j * j <= i;j++){
if(i % j == 0){
dp[i] += dp[j];
if(i / j != j && j != 1){
dp[i] += dp[i / j];
}
}
}
dp[i] += cnt;
dp[i] /= cnt - 1;
}
}
int main()
{
init();
int T;
int cas = 1;
cin>>T;
while(T--){
int n;
scanf("%d",&n);
printf("Case %d: ",cas++);
printf("%.12lf\n",dp[n]);
}
return 0;
}
Light 1248
题意:给一枚n面的骰子,求期望次数使得所有n面都至少朝上过一次。
思路: \(dp[i][j] 表示i面的骰子的j面至少朝上过一次的期望次数\)
\(dp[i][1] = 1\)
\(dp[i][j] = \frac{i-j+1}{i}(dp[i][j]+1) + \frac{j-1}{i}(dp[i][j-1] + 1) (2<=j<=i)\)
化简得\(dp[i][j] = dp[i][j-1] + \frac{i}{j-1}\)
于是答案就等于\(\sum_{i=1}^{n}\frac{n}{i}\)
Light Oj 1079
题意:一个强盗想要抢劫几家银行,每家银行有一定的存款数,且每抢劫一家银行都有一定概率被抓获,问在给定的安全概率下,强盗所能抢劫到的最大钱数。且抢劫各个银行被抓的概率是相互独立的。
思路: 容量为浮点数的背包问题
由于这里钱的总数比较小,把钱看成容量,dp[i]表示抢到钱i的最小被抓概率
从大到小枚举一下最抓概率小于安全概率的就是答案
若钱也为浮点数就不能这样解了,这篇博客讲了连续值背包的求解问题
const int N = 1e4 + 10;
double dp[N];
double P;
int n;
double p[N];
int w[N];
int main()
{
int T;
int cas = 1;
cin>>T;
while(T--){
scanf("%lf%d",&P,&n);
int sum = 0;
for(int i =1;i <= n;i++){
scanf("%d%lf",w + i,p + i);
sum += w[i];
}
for(int i = 0;i <= sum;i++) dp[i] = 1000000;
dp[0] = 0;
for(int i = 1;i <= n;i++){
for(int j = sum;j >= w[i];j--)
dp[j] = min(dp[j],p[i] + (1 - p[i]) * dp[j - w[i]]);
}
int ans = 0;
for(int i = sum;i >= 0;i--) if(dp[i] < P) {
ans = i;
break;
}
printf("Case %d: %d\n",cas++, ans);
}
return 0;
}
Light Oj 1265
题意: 有一个人在荒岛上求生,岛上有t只老虎,d只鹿
每天随机的会有两只动物相遇
>- 老虎和老虎相遇,相互残杀,最后都会死亡
>- 老虎和人相遇,人被老虎吃了
>- 老虎和鹿相遇,鹿被老虎吃了
>- 鹿和鹿相遇,什么也没发生
>- 人和鹿相遇,人可以选择吃或者不吃鹿
问你当人能肯定自己一定能存活下来的最大概率是多少?
思路: 首先奇数只老虎,人一定不能存活。
偶数只老虎相互残杀最后变成0,那其实也就是求这个老虎相互残杀变成0只的概率
从长远来看,很久很久以后,鹿一定被老虎吃光了,那么就只剩人和老虎了,这个时候就相当于每次选两次老虎直到为0,即C(t,2) / C(t + 1,2)的累乘,因为这里求的是概率,所以若干年后概率是不会变的,如果求的是年数的期望,就不可行了。
另一种更好想的做法就是dp[i][j]表示剩i只老虎j只鹿,人存活的概率
考虑转移每次要么死两只老虎,要么死一只鹿,死的这只鹿可以是老虎杀的,也可以是人杀的
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1001;
double dp[N][1001];
double C(int n){
if(n < 2) return 0;
return 1.0 * n * (n - 1) / 2;
}
int main()
{
int T, cas = 1;
cin>>T;
while(T--){
int t, d;
scanf("%d%d",&t,&d);
printf("Case %d: ",cas++);
if(t % 2){
printf("0\n");
continue;
}
/*memset(dp, 0, sizeof(dp));
for(int i = 0;i <= d;i++) dp[0][i] = 1;
for(int i = 1;i <= t;i++){
for(int j = 0;j <= d;j++){
double a = 0,b = 0;
int suma = C(i+j+1) - C(j) - j, sumb = suma + j;
if(j) a += i * j * dp[i][j-1],b += i * j * dp[i][j-1] + j * dp[i][j-1];
if(i>=2) a += C(i) * dp[i - 2][j],b += C(i) * dp[i - 2][j];
if(suma > 0) a /= suma;
if(sumb > 0) b /= sumb;
dp[i][j] = max(a, b);
}
}*/
double ans = 1;
while(t) ans *= 1.0 * (t - 1) / (t + 1),t -= 2;
printf("%.12lf\n",ans);
}
return 0;
}