1 solutions

  • 0
    @ 2025-5-4 22:38:32

    仔细读题,分析长颈鹿购买的流程,不难发现:只要第一次购买的饮料型号确定了下来,后面所有的购买流程以及会购买的饮料型号全都会确定下来。因为从第二次开始,所有的窗口都至多有一种有存货的饮料。

    所以我们只需要从 1n1\sim n 进行枚举,每次分别枚举其出货正确或者出货错误时的购买流程,就可以计算其在这种情况下支出的费用。

    每次具体模拟如下:首先支付对应费用,然后判断当前买到了哪个款式,对应款式设置为缺货状态,然后长颈鹿跳转到下一个窗口。每次在支付费用之后先判断,如果该窗口提供的两种饮料都缺货,则直接中断;反之,判断哪一种有存货,购买到对应的饮料种类,然后到对应的窗口。直至购买到的饮料是目标饮料为止。

    最终结果的计算只要会数学期望的基础即可。把上面的每一种情况乘以该窗口第一次出货正确或者错误的概率。所有项加和之后除以 nn 即为所求。

    最后注意一下输出答案,不能和标准答案超过 10610^{-6} ,所以要输出小数点后 66 位及以上。且为了精度问题,必须使用 double 或者 long double 类型,实测 float 的精度无法支持这样的运算。

    本题并没有想象中的那么签到,不过大家也要稳住心态,只要细心读题,慢慢构造模拟流程即可。

    #include <stdio.h>
    #include <string.h>
    #define N 2010
    int n, x;
    int a[N], b[N];
    double p[N];
    double ans;
    char remain[N]; // 剩余哪些有存货的饮料
    // 模拟 st : 起始所在窗口 st_right : 是否出货正确的饮料
    int simulation(int st, char st_right)
    {
        memset(remain, 1, n + 1); // 初始将所有饮料设置为有存货
        int ret = a[st], buy = st_right ? st : b[st]; // 起始需要花钱
        remain[buy] = 0;
        while (buy ^ x)
        {
            st = buy;
            ret += a[st]; // 先花钱,再判断是否有警报
            if (!remain[st] && !remain[b[st]])
                break;
            buy = remain[st] ? st : b[st];
            remain[buy] = 0;
        }
        return ret;
    }
    int main()
    {
        scanf("%d%d", &n, &x);
        for (int i = 1; i <= n; ++i)
            scanf("%d%d%lf", &a[i], &b[i], &p[i]);
        for (int i = 1; i <= n; ++i)
            ans += simulation(i, 1) * p[i] + simulation(i, 0) * (1 - p[i]);
        // 由于和答案误差不超过 10^-6 就算正确,只要输出 6 位即以上小数即可。必须使用 `double` 或者 `long double`
        printf("%.7f", ans / n); // 数学期望,不要忘记除以 n
    }
    
    • 1

    Information

    ID
    12
    Time
    1000ms
    Memory
    512MiB
    Difficulty
    1
    Tags
    # Submissions
    5
    Accepted
    1
    Uploaded By