zoukankan      html  css  js  c++  java
  • 牛客题单__动态规划课程概率dp例题

    牛客题单__动态规划课程概率dp例题

    NC15532 Happy Running

    大意:

    问围着一个x的跑道跑顺时针跑,打卡完毕的时候跑k米及以上的概率。两个打卡点是随机的,必须先打第一个再打第二个,如果先跑到第二个,那么也要先打完第一个点然后再绕一圈到第二个点打卡

    思路:

    当k<=x时,如果两个点都在前k米,并且第一个点在第二个点前面,那么不行

    所以概率为0.5+0.5 * (1-两个都在前k米的概率)

    当k大于2x时,必然跑不到

    介于x到2x之间时,需要第2个点在第1个点前面,同时两个点都不能在前(2*k-x)米

    #include <bits/stdc++.h>
    using namespace std;
    
    int t;
    double k, x;
    
    int main(void) {
        cin >> t;
        while (t--) {
            cin >> k >> x;
            double res = 0;
            if (k <= x) {
                res = 0.5;
                res += 1.0 * (1.0 * (x - k) / x) * (1.0 * (k + x) / 2 / x);
            } else if (k <= 2 * x) {
                res += 1.0 * (1.0 * (2 * x - k) / x) * (1.0 * (2 * x - k) / 2 / x);
            }
            printf("%.2lf
    ", res);
        }
        return 0;
    }
    
    

    POJ2096 Collecting Bugs

    大意:

    现在有很多bug,按照位置分可以分为n类,按照类型分可以分为m类,现在有一个人,每天可以找到一个bug,问找到全部n个位置m个类型至少各有一个bug,花费天数的期望

    思路:

    图片说明

    但是(f[i][j])等式右边也有(f[i][j]),不好转移,所以将其移项到左边,化简一下即可转移了

    写的时候大爆搜即可

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <queue>
    #include <string>
    using namespace std;
    
    const int N = 1e3 + 5;
    typedef long long LL;
    double f[N][N];
    int n, m;
    
    double dp(int x, int y) {
        if (f[x][y] != 0) return f[x][y];
        if (x == n && y == m) return 0;
        if (x > n || y > m) return 0;
        double p1 = ((1.0 * x / n )* (1.0 * y / m));
        double p2 = ((1.0 * x / n )* (1.0 - 1.0 * y / m));
        double p3 = ((1.0 - 1.0 * x / n) * 1.0 * y / m);
        double p4 = ((1.0 - 1.0 * x / n) * (1.0 - 1.0 * y / m));
        return f[x][y] = (p1 + p2 * (dp(x, y + 1) + 1.0) + p3 * (dp(x + 1, y) + 1.0) +
                          p4 * (dp(x + 1, y + 1) + 1.0)) /
                         (1.0 - p1);
    }
    
    int main() {
        memset(f, 0, sizeof f);
        cin >> n >> m;
        printf("%.4f
    ", dp(0, 0));
        return 0;
    }
    

    NC210477 带富翁

    大意:

    小明在玩一款带富翁游戏,这个游戏具体来说就是有n个奖励点,每个奖励点有一定的奖励分。一开始他站在位置1。每次他都会扔一个有6面的筛子,如果扔到了x,并且小明现在站在i这个位置,小明就会向前进x步到达i+x这个位置。

    如果小明扔出了x并且i+x已经大于了n,那么小明就会重新扔,直到i+x在合适的位置。小明最后如果到达了n号位置,那么小明就会结束游戏,现在小明想知道自己期望的得分是多少。

    思路:

    还是大爆搜就行,注意如果当前位置和n之间的距离小于6,那么转移就不是1/6的概率了,而是1/(n-now)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    double a[N], f[N];
    int n;
    
    double dp(int now) {
        if (f[now] != 0) return f[now];
        if (now > n) return 0;
        if (now == n) return a[now];
        double res = 0;
        int cnt = min(n - now,6);
        for (int i = 1; i <= cnt; i++) {
            res += dp(now + i);
        }
        return f[now] = res*(1.0 / cnt)+a[now];
    }
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        printf("%.8lf", dp(1));
        return 0;
    }
    

    NC210481 筛子游戏

    大意:

    一开始你会得到三个筛子,三个筛子分别有k1,k2,k3面,也就是说分别可以扔出[1,k1],[1,k2],[1,k3]之间数。
    一开始的分数为0,每次扔筛子都会扔出x,y,z三个数,但是这个游戏的特别之处在于每次开局都会给定三个数a,b,c如果满足x=a,y=b,z=c那么你的分数就会清零,否则你的分数就会加上x+y+z。现在吉吉国王想知道需要扔多少次才能使得他的分数大于n。

    思路:

    (f[now])为当前分数为now时,需要扔多少次,那么(f[now])可以转移到(f[now+k])的位置,也可以转移到(f[0]):

    (f[i]=∑(p[k]*f[i+k])+f[0]*p[0]+1)

    其中(p[k])为转移的概率,可以看到(f[i])都和(f[0])有关,但是所求即为(f[0]),没法直接递推求解,可以先将(f[0])设置为一个常数:

    (f[i]=A[i]*f[0]+B[i]),将其代入到上式右侧:

    (f[i]=∑(p[k]*A[i+k]*f[0]+p[k]*B[i+k])+f[0]*p[0]+1)

    化简:

    (f[i]=(∑(p[k]*A[i+k])+p[0])f[0]+∑(p[k]*B[i+k])+1)

    所以:

    (A[i]=(∑(p[k]*A[i+k])+p[0]),B[i]=∑(p[k]*B[i+k])+1)

    所以首先打表求出(p[k]),然后递推求出A和B数组,最后通过(f[0]=B[0]/(1-A[0]))来求解出(f[0])即可

    #include <bits/stdc++.h>
    using namespace std;
    double p[20];
    double A[550], B[550];
    int main() {
        int n, k1, k2, k3, a, b, c;
        scanf("%d%d%d%d%d%d%d", &n, &k1, &k2, &k3, &a, &b, &c);
        p[0] = 1.0 / k1 / k2 / k3;
        for (int i = 3; i <= k1 + k2 + k3; i++) {
            int cnt = 0;
            for (int j = 1; j <= k1; j++)  //第一个骰子
            {
                for (int k = 1; k <= k2; k++)  //第二个骰子
                {
                    int l = i - j - k;
                    if (l < 1 || l > k3) continue;
                    if (j == a && k == b && l == c) {
                        //如果会跳到0,不统计
                    } else if (j + k + l == i)  //三个骰子之和等于i
                    {
                        cnt++;
                    }
                }
            }
            p[i] = 1.0 * cnt / k1 / k2 / k3;
        }
        for (int i = n; i >= 0; i--)  //递推
        {
            for (int k = 3; k <= k1 + k2 + k3; k++)  //枚举骰子点数总和
            {
                A[i] += p[k] * A[i + k];
                B[i] += p[k] * B[i + k];
            }
            A[i] += p[0];
            B[i] += 1.0;
        }
        double ans = B[0] / (1 - A[0]);
        printf("%.10lf
    ", ans);
    }
    

    NC210487 食堂

    大意:

    食堂排队可以看成一个长度为n的队列,一开始吉吉国王站在m这个位置上,一般来说,窗口前的第一个人在打饭的时候会发生四种情况。
    第一种情况是打饭的时候窗口没人,这个时候要等待一会儿,发生的概率是p1。
    第二种情况是发现自己没带饭卡,这个时候就要回去拿饭卡并且排到了队列的末尾,发生的概率是p2。(这里认为每个人只有在即将打饭的时候才会去摸饭卡,只有这时才有发现自己没带饭卡的机会。)
    第三种情况是打饭成功,这个时候队列的长度减一,发生的概率是p3。
    第四种情况是食堂关门,这个时候大家都不能打饭了,发生的概率是p4。
    吉吉国王老倒霉蛋了,经常在食堂关门的时候排在队伍的前面,因此他想知道这样的事件发生的概率。现在你需要告诉吉吉国王在食堂关门时他排在队伍的前k位的概率。

    思路:

    图片.png 图片.png
    #include <bits/stdc++.h>
    using namespace std;
    double dp[2010][2010];
    double c[2010], p[2010];
    #define eps 1e-10
    
    int main() {
        int n, m, k;
        double p1, p2, p3, p4;
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cin >> n >> m >> k >> p1 >> p2 >> p3 >> p4;
        if (p4 < eps) {
            cout << "0.00000
    ";
            return 0;
        }
        double p12 = p2 / (1 - p1);
        double p13 = p3 / (1 - p1);
        double p14 = p4 / (1 - p1);
        p[0] = 1;
        for (int i = 1; i <= n; i++) {
            p[i] = p[i - 1] * p12;
        }
        dp[1][1] = p4 / (1 - p1 - p2);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                if (j <= k) {
                    c[j] = dp[i - 1][j - 1] * p13 + p14;
                } else
                    c[j] = dp[i - 1][j - 1] * p13;
            }
            double temp = 0;
            for (int j = 1; j <= i; j++) {
                temp += p[i - j] * c[j];
            }
            dp[i][i] = temp / (1 - p[i]);
            dp[i][1] = p12 * dp[i][i] + p14;
            for (int j = 2; j < i; j++) {
                dp[i][j] = dp[i][j - 1] * p12 + c[j];
            }
        }
        cout << fixed << setprecision(5) << dp[n][m];
        return 0;
    }
    
  • 相关阅读:
    H5前端上传文件的几个解决方案
    MyEclipse和Eclipse各个版本的汉化破解(包括7.59.0和Eclips的ehelios、indigo各版本)
    学习C#中的事件和委托
    JS学习笔记(一)JS处理JSON数据
    图解Eclipse开发C++、C语言的配置过程使用CDT和MinGw
    ASP.NET使用C#实现的最简单的验证码方法
    微信公众号支付|微信H5支付|微信扫码支付|小程序支付|APP微信支付解决方案总结
    PHP开发笔记(二)PHP的json_encode和json_decode问题
    C博客作业00——我的第一篇博客
    extern "c"
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14465574.html
Copyright © 2011-2022 走看看