期望DP初步

感觉期望DP这种东西像是玄学…
主要总结说一点基础性的东西, 或许对于理解题目的做法会有一点帮助.

首先是关于独立事件, 互斥事件的概念. 通俗地说, 就是对于两个事件A, B, 假如满足发生了其中一个就不会发生另一个, 则称A, B为互斥事件; 假如A与B的发生没有任何关系, 可能都发生, 可能只有一个发生, 也可能都不发生, 则称A, B为独立事件. 下文的讨论主要针对独立事件进行.

一. 事件间的关系与运算;
A + B (和事件): 表示A, B两个事件至少有一个发生;
A · B(积事件)表示A, B两个事件同时发生;

二. 复杂事件的概率运算公式
和事件的概率: P(A + B) = P(A) + P(B) - P(A * B)
推广得到: P(A)=(P(A+B)−P(B))/(1−P(B))
即: P(A - B) = (P(A) - P(B)) / (1 - P(B))
适用于在知道和事件与其中一个事件的概率的情况下, 求另一个事件的概率.
特别地, 当A与B为互斥事件时, P(A + B) = P(A) + P(B)
积事件的概率: P(A1 * A2 * A3 * … * An) = P(A1) * P(A2) * … * P(An)

三. 期望值的计算公式:
Ex = ΣXiPi(ΣPi = 1)

例题: BZOJ3566概率充电器
这一题就用到了和公式与差公式

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
inline int read()
{
    int x = 0, flag = 1;
    char c;
    while(! isgraph(c = getchar()))
        if(c == '-')
            flag *= - 1;
    while(isgraph(c))
        x = x * 10 + c - '0', c = getchar();
    return x * flag;
}
void println(int x)
{
    if(x < 0)
        putchar('-'), x *= - 1;
    if(x == 0)
        putchar('0');
    int top = 0, ans[1 << 4];
    while(x)
        ans[top ++] = x % 10, x /= 10;
    for(; top; top --)
        putchar(ans[top - 1] + '0');
    putchar('\n');
}
const double EPS = 1e-8;
const int MAXN = 1 << 20;
int head[MAXN];
int top;
struct edge
{
    int v, next;
    double p;
}T[MAXN << 1];
void add_edge(int u, int v, double p)
{
    T[top].v = v, T[top].next = head[u], T[top].p = p, head[u] = top ++;
}
double q[MAXN];
double f[MAXN], ans[MAXN];
void DFS(int u, int fa)
{
    f[u] = q[u];
    for(int i = head[u]; i != - 1; i = T[i].next)
    {
        int v = T[i].v;
        if(v == fa)
            continue;
        DFS(v, u);
        f[u] = f[u] + f[v] * T[i].p - f[u] * f[v] * T[i].p;
    }
}
void DGS(int u, int fa)
{
    for(int i = head[u]; i != - 1; i = T[i].next)
    {
        int v = T[i].v;
        if(v == fa)
            continue;
        if(1 - f[v] * T[i].p < EPS)
                ans[v] = 1.0;
        else
        {
            double x = (ans[u] - f[v] * T[i].p) / (1 - f[v] * T[i].p) * T[i].p;
            ans[v] = f[v] + x - f[v] * x;
        }
        DGS(v, u);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("BZOJ3566.in", "r", stdin);
    freopen("BZOJ3566.out", "w", stdout);
    #endif
    int n = read();
    memset(head, - 1, sizeof(head));
    top = 0;
    for(int i = 1; i < n; i ++)
    {
        int a = read(), b = read(), p = read();
        add_edge(a, b, (double)p / 100.0);
        add_edge(b, a, (double)p / 100.0);
    }
    for(int i = 1; i <= n; i ++)
        q[i] = (double)read() / 100.0;
    memset(f, 0, sizeof(f));
    DFS(1, 1);
    ans[1] = f[1];
    DGS(1, 1);
    double sum = 0;;
    for(int i = 1; i <= n; i ++)
        sum += ans[i];
    printf("%.6f", sum);
}

### 数据结构与算法中的凹凸性 #### 凹凸性的定义 在数学分析中,函数的凹凸性描述的是曲线弯曲的方向。对于给定区间内的连续二阶可导函数 \( f(x) \),如果其二阶导数 \( f''(x) > 0 \),则该函数在此区间内为严格凸;反之,若 \( f''(x) < 0 \),则此函数为严格凹。 然而,在计算机科学领域特别是数据结构和算法设计方面,“凹凸性”的概念更多体现在某些特定应用场景下对数值变化趋势的要求上: - **单调栈**:利用栈来维护一组具有某种顺序关系的数据项(如递增或递减),从而快速判断新加入元素相对于已有集合的位置特性; - **动态规划中的决策路径优化**:一些经典的DP题目会涉及到求解最短路经、最小费用等问题时,可能会遇到需要考虑局部最优解组合成全局最优解的情况,此时往往可以通过证明目标函数满足一定的凹凸性质来进行剪枝操作以减少不必要的状态转移计算量[^1]。 #### 应用实例 ##### 单调队列/堆的应用场景 在一个长度固定的滑动窗口里寻找最大值或者最小值问题中,可以采用双端队列作为辅助工具,保持内部存储着当前可见范围内按降序排列的关键码序列。每当有新的候选者进入视野时就将其依次同队尾成员比较直至找到合适位置插入进去,并把那些已经超出界限的老记录弹出去。由于每次调整都遵循先进先出原则加上维持有序性不变的特点使得查询复杂度稳定于O(nlogn)[^2]。 ```cpp deque<int> dq; for(int i=0;i<n;++i){ while(!dq.empty() && a[dq.back()]<=a[i]) dq.pop_back(); dq.push_back(i); if(dq.front()<=(i-k)) dq.pop_front(); // remove out-of-bound elements } ``` ##### 动态规划中的应用 假设存在这样一个背包容量有限制的问题——即每种物品都有固定的价值v[]以及重量w[],现在要挑选若干件放入其中让总价值尽可能高但不超过限定载重m。这里就可以借助贪心策略配合优先级队列完成初步筛选工作之后再运用分治法细究剩余可能性空间里的极值分布规律进而得出结论。而在这个过程中为了加快收敛速度通常还会引入松弛变量r表示已知最好方案下的预期收益增量期望值,随着迭代次数增加逐渐逼近真实答案直到两者差距小于预设阈值为止停止运算返回结果。 ```python def knapsack(weights, values, capacity): n = len(values) dp = [[0]*(capacity+1) for _ in range(n+1)] for i in range(1,n+1): wi,vi = weights[i-1],values[i-1] for j in range(capacity,-1,-1): if j >= wi: dp[i][j]= max(dp[i-1] weights=[2,3,4,5] value =[3,4,8,8] print(knapsack(weights,value,5)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值