算法竞赛进阶指南 0x38 概率与数学期望

相关概念

  1. 样本点:随机试验中可能的结果。
  2. 样本空间:随机试验中所有可能情况构成的集合。
  3. 随机事件:样本空间的子集。(是由样本点构成的集合)
  4. 随机变量:把样本点映射为实数的函数。
    • 离散型(有限个或者是可数的【阿列夫零】)
    • 连续型
  5. 概率的定义:

设样本空间为Ω,对于该样本空间中的任意一个随机事件,都有实值函数 P ( A ) P(A) P(A)使得
P(A)大于等于0
P(Ω) 等于1
对于两个互斥事件(不可能同时发生),两个事件所组成的随机事件概率是两个随机事件分别发生的概率之和。

  1. 数学期望:

即随机变量的取值以及期望的乘积之和。
**性质:**期望是线性函数
eg. 对于投掷两枚骰子,点数之和的数学期望就相当于是投掷一枚骰子所得点数的期望乘以2.

AcWing216. Rainbow的信号

图片.png图片.png

思路

注意位运算的性质:位运算的一位上的数字不会影响其他位数。
计算可能情况的概率

  • 对于长度为1的区间,概率为 1 N 2 1\over{N^2} N21
  • 对于长度大于1的区间,概率为 2 N 2 2\over{N^2} N22

然后累加所有情况乘以概率即可。
但是如果使用 O ( N 2 ) O(N^2) O(N2)的方法,肯定超时。
这个时候,使用位运算的性质来减少复杂度。
不妨使用last[x],x取 0 或者 1 来表示上一个0/1所在的位置。
第一步:扫描所有长度为1的区间,如果是1,那么就累加 1 N 2 {1}\over{N^2} N21
第二步:(采用了数学运算来对遍历进行了简化)

  1. 对于与运算
    1. 如果这一位是1,那么答案累加 2 k ∗ ( ( r − 1 ) − ( l a s t [ 0 ] + 1 ) + 1 ) ∗ 2 N 2 2^k*{( (r-1) - (last[0]+1) +1)*2}\over{N^2} N22k((r1)(last[0]+1)+1)2
  2. 对于或运算
    1. 如果这一位是1,那么答案累加 2 k ∗ ( r − 1 ) ∗ 2 N 2 2^k*{(r-1)*2}\over{N^2} N22k(r1)2
    2. 如果这一位是0,那么答案累加 2 k ∗ ( l a s t [ 1 ] ) ∗ 2 N 2 2^k*{(last[1])*2}\over{N^2} N22k(last[1])2
  3. 对于异或运算,有一些复杂

图片.png
摘抄自算法竞赛进阶指南

代码

#include <bits/stdc++.h>
using namespace std;
#define N 100020
int s[N];
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", s+i);
    double ansxor, ansor, ansand;
    ansand = ansor = ansxor = 0;
    for(int i = 0; i <= 31; i ++)//我在这里先处理or和and
    {
        for(int j = 1; j <= n; j++)
        {
            if((s[j] >> i) & 1)
            {
                ansand += (double)(1<<i)/n/n;
                ansor += (double)(1<<i)/n/n;
                ansxor += (double)(1<<i)/n/n;
            }
        }
        int last[2] = {0};
        for(int j = 1; j <= n; j++)
        {
            if((s[j]>>i) & 1)
            {
                //if(last[0]!=0)//这句话其实并没有必要,因为
                /*
                1.当j为1的时候,得到的答案是0
                2.当j>1的时候,正好复合预期!
                */
                    ansand+=(double)(1<<i)*2*(j-last[0]-1)/n/n;
                ansor += (double)(1<<i)*(j-1)*2/n/n;
                last[1] = j;
            }
            else
            {
                ansor += (double)(1<<i)*last[1]*2/n/n;
                last[0] = j;
            }
        }
        int c1, c2;
        c1 = 0;
        c2 = 0;
        for(int j = 1; j <= n; j++)
        {
            if((s[j] >> i)&1)
            {
                ansxor += (double)(1<<i)*c1*2/n/n;
                c1++;
                swap(c1, c2);
            }
            else
            {
                ansxor += (double)(1<<i)*(c2)*2/n/n;
                c1++;
            }
        }
    }
    printf("%.3lf %.3lf %.3lf", ansxor, ansand, ansor);
    return 0;
}

AcWing217. 绿豆蛙的归宿

图片.png
图片.png

思路

当提及到有向无环图,我就立即想起了拓扑序。
拓扑序并不一定是要把序列存起来,然后再遍历一遍。而是在过程中就可以进行遍历。

代码

#include <bits/stdc++.h>
using namespace std;
#define N 200020
int ver[N], edge[N], head[N], nxt[N];
int tot;
int deg[N], out[N];
void add(int x, int y, int w)
{
    ver[++tot] = y;
    edge[tot] = w;
    nxt[tot] = head[x];
    head[x] = tot;
}
double dis[N];
stack<int> st;
int main()
{
    
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(b, a, c);//反过来建图
        deg[a]++;
        out[a]++;
    }   
    st.push(n);
    while(!st.empty())
    {
        int t = st.top();
        st.pop();
        for(int i = head[t]; i; i = nxt[i])
        {
            int xx = ver[i];
            dis[xx] += (double)(edge[i]+dis[t])/deg[xx];
            out[xx]--;
            if(out[xx]==0) st.push(xx);
        }
    }
    printf("%.2lf", dis[1]);
    return 0;
}

AcWing218. 扑克牌

图片.png
图片.png

思路

这道题目是关于动态规划的,我浅浅地参考一下标程。

代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值