【笔记】概率与期望

概率

某个事件 \(A\) 发生的可能性的大小,称之为事件 \(A\) 的概率,记作 \(P(A)\)。

  • 假设某事的所有可能结果有 \(n\) 种,事件 \(A\) 涵盖其中的 \(m\) 种,那么 \(P(A)=m/n\)。
  • 例如投掷一枚骰子,点数小于 \(3\) 的概率为 \(2/6=1/3\)。

如果两个事件A和B所涵盖的结果没有交集,那么 \(P\) ( \(A\) 或 \(B\) 发生) $ = P(A)+P(B)$。

还是掷骰子:

  • \(P\) (点数小于 \(3\) 或点数大于 \(4\) ) \(= 2/6 + 2/6 = 2/3\)。
  • 如果 \(A\) 和 \(B\) 所涵盖的结果有交集。
  • 那么 \(P\) (\(A\) 或 \(B\) 发生) $ = P(A) + P(B) - P$ (\(A\) 与 \(B\) 同时发生)。
  • \(P\) (点数小于 \(3\) 或点数为偶数) \(=2/6+3/6-1/6=2/3\)。

记事件 \(B\) 为“事件 \(A\) 不发生” :

  • 那么 \(P(A)+P(B)=1\),即 \(P(B)=1-P(A)\)。
  • \(P\) (点数不小于 \(3\) ) \(=1-2/6=2/3\)。
  • 在两个互不干扰的事中,事件 \(A\) 在其中一件事中,事件 \(B\) 在另外一件事中。
  • 那么 \(P\) (\(A\) 与 \(B\) 同时发生) \(=P(A)*P(B)\)。
  • 掷两个骰子,\(P\) (第一个点数小于 \(3\) 且第二个点数为偶数) \(=(2/6)*(3/6)=1/6\)。

期望

事件 \(A\) 有多种结果,记其结果的大小为 \(x\),那么 \(x\) 的期望值表示 事件 \(A\) 的结果的平均大小,记作E(x)。

  • \(E(x)=\) 每种结果的大小与其概率的乘积的和。
  • 例如,记掷一枚骰子的点数为 \(x\)。
  • \(E(x)=1 \times \dfrac{1}{6} +2 \times \dfrac{1}{6}+3 \times \dfrac{1}{6} + 4 \times \dfrac{1}{6} + 5 \times \dfrac{1}{6} + 6 \times \dfrac{1}{6} = \dfrac{7}{2}\)
  • 若 \(c\) 为常数,那么:
  • \(E(x+c)=E(x)+c,E(c \times x)=c \times E(x)\)。

因为如果我们把我们掷骰子掷出的点数统一加一个常数 \(c\),再掷一次骰子,那么我们发现这样统计出来的期望正好是原先期望 \(+ c\),乘法同理。

记两个事件的结果分别为 \(x\),\(y\)。

  • \(E(x+y)=E(x)+E(y)\)。
  • 例如:\(E\) (语文成绩 + 数学成绩)= \(E\) (语文成绩) + \(E\) (数学成绩)
  • 若两个事件互相独立,\(E(x \times y) = E(x) \times E(y)\)。
  • \(E\) (语文成绩 \(\times\) 数学成绩) \(= E\) (语文成绩) \(\times E\) (数学成绩)。

概率与期望的计算

概率与期望的计算有一个共同的计算技巧:

  • 若事件所产生的所有方案都是等概率的,那么一些概率与期望即可转化为一个计数问题,算出后再除以总方案数即可。
  • 如求事件符合条件 \(A\) 的概率,则转化为对符合 \(A\) 的 方案数的计数问题;若求方案的某种价值的期望值,则转化为所有方案的 价值总和的计数问题。
  • 概率与期望的计算也经常用的其加法和乘法规则。
  • 尤其是期望的加法规则,在期望的计算中十分常用。如求最大值与最小值之差的期望,则分别求二者的期望值再作差即可。
  • 应用乘法规则时,要注意事件是否互相独立。

列方程法

概率与期望还可以通过列方程的方法计算。

有 \(4\) 张卡片,写着 \(0,1,2,3\),每次抽出一张并放回,反复抽,抽出 \(0\) 为止。问抽取的次数的期望值 :

  • 设抽取次数为 \(x\),则:
  • \(x=1+x \times \dfrac{3}{4}\)。
  • \(x=4\)。
  • \(x=1+ x \times \dfrac{3}{4} + 0 \times \dfrac{1}{4}\)。

【钉子和小球】

【\(Description\)】

咕咕咕

【\(Solution\)】

首先我们看到小球在满钉子的情况下落在第 \(i\) 个格子中的概率 \(p_i = \dfrac{C_n ^ i}{2 ^ n} = \dfrac{n!}{2 ^ n i ! (n - i) !}\)

首先可以发现小球落在第一个位置的概率是 1,接着向下推,发现有两个性质 :

  • 如果小球下面有钉子,那么小球往两边滚动的概率是一样的,所以我们把上面的概率除以 2,平分到下面即可。
  • 如果下面没有钉子,显然可以直接掉落到两格下面,直接转移即可。

换成 DP 来说就是 :

设初始的 \(f[0][1].son 和 f[0][1].mu\) 为 1。
之后 \(n ^ 2\) 枚举转移,遇到钉子就 \(f[i][j] += \dfrac{f[i - 1][j]}{2}\),否则 \(f[i + 1][j + 1] += f[i - 1][j]\)

因为每一层的钉子数不相同,所以从上面转移到下面的时候横坐标要加 1。

不过这种重载运算符我是真的不想写,一写就写错。

【\(Code\)】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long 
using namespace std;
const int Maxk = 1005;
int n,m;
int a[Maxk][Maxk];
inline int GCD(int x,int y) {return y == 0 ? x : GCD(y,x % y);}
struct Node{
  int x_,y_;
  void Gcd() {
    int k = GCD(x_,y_);
    x_ /= k;
    y_ /= k;
  }
  Node operator /(const int z) const {
    Node now = *this;
    now.y_ *= z;
    //cout << now.x_ << " " << now.y_ << endl;
    now.Gcd();
    return now;
  }
  Node operator +(Node now)const{
    if(x_ == 0) {
      now.Gcd();
      return now; 
    }
    Node th = *this;
    if(now.x_ == 0) {
      th.Gcd();
      return th;
    }
    int k = GCD(th.y_,now.y_);
    th.x_ *= (now.y_ / k);
    th.x_ += now.x_ * (th.y_ / k);
    th.y_ *= (now.y_ / k);
    th.Gcd();
    return th;
  }
}f[Maxk][Maxk]; 
inline int read()
{
  int f = 0,s = 0;char ch = getchar();
  while(!isdigit(ch)) f |= ch == '-',ch = getchar();
  while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48),ch = getchar();
  return f ? -s : s; 
} 
signed main()
{
  n = read(),m = read();
  for(int i = 1;i <= n;i ++) {//毒瘤的读入 
    for(int j = 1;j <= i;j ++) {
      char s;
      while((s = getchar()) != '*' && s != '.');
      if(s == '*') a[i][j] = 1;
    }
  }
  f[0][1].x_ = 1;
  f[0][1].y_ = 1;
  for(int i = 1;i <= n + 1;i ++) {
    for(int j = 1;j <= n + 1;j ++) {
      f[i][j].y_ = 1;
    }
  }
  for(int i = 1;i <= n;i ++) {
    for(int j = 1;j <= i;j ++) {//一行一列开始搜。 
      if(a[i][j]) {
        Node cur = f[i - 1][j];//这一行上面的钉子分向两边 
        cur = cur / 2;//概率除 2
        f[i][j] = f[i][j] + cur;
        f[i][j + 1] = f[i][j + 1] + cur;
      } 
      else {
        f[i + 1][j + 1] = f[i + 1][j + 1] + f[i - 1][j];
      } 
    } 
  }
  for(int i = 1;i <= n;i ++) {
    for(int j = 1;j <= i + 1;j ++) {
      f[i][j].Gcd();
    }
  }
  f[n][m + 1].Gcd();
  cout << f[n][m + 1].x_ << "/" << f[n][m + 1].y_ << endl; 
  return 0;
} 

期望DP

【\(Description\)】

小 \(Q\) 同学现在沉迷炉石传说不能自拔。他发现一张名为克苏恩的牌很不公平。

如果你不玩炉石传说,不必担心,小 \(Q\) 同学会告诉你所有相关的细节。炉石传说是这样的一个游戏,每个玩家拥有一个 \(30\) 点血量的英雄,并且可以用牌召唤至多 \(7\) 个随从帮助玩家攻击对手,其中每个随从也拥有自己的血量和攻击力。

小 \(Q\) 同学有很多次游戏失败都是因为对手使用了克苏恩这张牌,所以他想找到一些方法来抵御克苏恩。他去求助职业炉石传说玩家椎名真白,真白告诉他使用奴隶主这张牌就可以啦。

如果你不明白我上面在说什么,不必担心,小Q同学会告诉你他想让你做什么。

现在小 \(Q\) 同学会给出克苏恩的攻击力是 \(K\) ,表示克苏恩会攻击 \(K\) 次,每次会从对方场上的英雄和随从中随机选择一个并对其产生 \(1\) 点伤害。

现在对方有一名克苏恩,你有一些奴隶主作为随从,每名奴隶主的血量是给定的。如果克苏恩攻击了你的一名奴隶主,那么这名奴隶主的血量会减少 \(1\) 点,当其血量小于等于 \(0\) 时会死亡,如果受到攻击后不死亡,并且你的随从数量没有达到 \(7\) ,这名奴隶主会召唤一个拥有 $34 点血量的新奴隶主作为你的随从;

如果克苏恩攻击了你的英雄,你的英雄会记录受到 \(1\) 点伤害。你应该注意到了,每当克苏恩进行一次攻击,你场上的随从可能发生很大的变化。

小 \(Q\) 同学为你假设了克苏恩的攻击力,你场上分别有 \(1\) 点、 \(2\) 点、 \(3\) 点血量的奴隶主数量,你可以计算出你的英雄受到的总伤害的期望值是多少吗?

【\(Solution\)】

【\(Code\)】

【换教室】

【\(Description\)】

先咕着

【\(Solution\)】

【\(Code\)】

【奖励关】

好像没看懂。。。

【OSU!】

【\(Solution\)】

来推式子。

假设我们已经知道了前 \(x\) 个操作的期望值,现在来求第 \(x + 1\) 个 :

\[(x + 1) ^ 3 \\= (x + 1)(x ^ 2 + 2x + 1) \\= x ^ 3 + x ^ 2 + 2x ^ 2 + 2x + x + 1 \\= x ^ 3 + 3x ^ 2 + 3x + 1 \]

于是我们发现每增加一个 1,我们的答案相比原答案多了 \(3x ^ 2 + 3x + 1\)。

所以我们可以来维护这个增加值的期望,并用递推的形式表现出来。

我们令 \(a[i]\) 为 \(x\) 的期望,\(b[i]\) 为 \(x ^ 2\) 的期望。

所以可以得到 :

\[a[i] = (a[i - 1] + 1) \times s[i] \\ b[i] = (b[i - 1] + 2 \times a[i - 1] + 1) \times s[i] \]

所以最终的答案就是 \(ans[i] = ans[i - 1] + (3 \times b[i - 1] + 3 \times a[i - 1] + 1) \times s[i]\)。

我们的答案就是 \(ans[n]\)。

【\(Code\)】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
const int Maxk = 1e5 + 10;
int n;
double s[Maxk]; 
double a[Maxk],b[Maxk],ans[Maxk];
inline int read()
{
  int f = 0,s = 0;char ch = getchar();
  while(!isdigit(ch)) f |= ch == '-',ch = getchar();
  while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48),ch = getchar();
  return f ? -s : s;
}
signed main()
{
  n = read();
  for(int i = 1;i <= n;i ++) scanf("%lf",&s[i]);
  for(int i = 1;i <= n;i ++) {
    a[i] = (a[i - 1] + 1) * s[i];
    b[i] = (b[i - 1] + 2 * a[i - 1] + 1) * s[i];
    ans[i] = ans[i - 1] + (3 * (a[i - 1] + b[i - 1]) + 1) * s[i];
  }
  printf("%.1lf",ans[n]);
  return 0;
}

上一篇:「AGC023E」Inversions【组合计数】


下一篇:CSMO 2021 游记