zoukankan      html  css  js  c++  java
  • Gym

    Gym - 102470D Darts 概率DP,思维

    题意

    (A,B) 两人轮流扔飞镖,初始分数均为(N) ,若射中的分数小于当前分数,则当前分数减去该分数,否则分数不变。

    现使得分数为(0) 的获胜,

    (A) 随机扔飞镖,每块的概率相等。

    (B) 可以贪心的选择三块区域扔飞镖,每块的概率(1/3)

    标靶的权值是固定的$1 $ 到 $20 $

    给定(N) ,问(A)(B) 先手分别可以获胜的概率

    分析

    此题引入了两个对象,也就是说每个人的状态会受到另一个的影响,还挺有意思的。

    首先需要注意到的是若两个人的分数都小于(20) ,那不论大小多少概率都是定值,因为仅当仍中与当前分数相等的时候才会获胜,和扔到多少无关。这是必须注意到的性质,至于是多少可以直接从样例复制。

    (dp1[i][j]) 表示(A) 的分数为(i) ,(B) 的分数为(j)(A) 先手获胜的概率,(dp2[i][j]) 也同理。

    转移方程

    [dp1[i][j] = sum_{k=0}^{k<20}(1-dp2[get(i,sc[k])][j])/20 \ dp2[i][j] = max(dp2[i][j],(3 - dp1[i][get(j,x)] - dp1[i][get(j,y)]-dp1[i][get(j,z)])/3) ]

    实现时注意转移的先后顺序(很重要!)

    const int sc[] = { 20,1,18,4,13,6,10,15,2,17,3,19,7,16,8,11,14,9,12,5 };
    
    int get(int x, int y) {
        return y <= x ? x - y : x;
    }
    
    double dp1[510][510], dp2[510][510];
    void init() {
        for (int i = 1; i <= 20; i++) for (int j = 1; j <= 20; j++) dp1[i][j] = 0.136363636364, dp2[i][j] = 0.909090909091;
        for (int i = 1; i < 510; i++) {
            for (int j = 1; j < 510; j++) {
                if (i <= 20 && j <= 20) continue;
                if (i <= 20) {
                    for (int k = 0; k < 20; k++) {
                        int y = (k + 1) % 20, z = (k + 2) % 20;
                        double tmp1 = dp1[i][get(j, sc[k])];
                        double tmp2 = dp1[i][get(j, sc[y])];
                        double tmp3 = dp1[i][get(j, sc[z])];
                        dp2[i][j] = max(dp2[i][j], (3 - tmp1 - tmp2 - tmp3) / 3.0);
                    }
                    for (int k = 0; k < 20; k++) {
                        dp1[i][j] += (1 - dp2[get(i, sc[k])][j]) / 20;
                    }
                }
                else {
                    for (int k = 0; k < 20; k++) {
                        dp1[i][j] += (1 - dp2[get(i, sc[k])][j]) / 20;
                    }
                    for (int k = 0; k < 20; k++) {
                        int y = (k + 1) % 20, z = (k + 2) % 20;
                        double tmp1 = dp1[i][get(j, sc[k])];
                        double tmp2 = dp1[i][get(j, sc[y])];
                        double tmp3 = dp1[i][get(j, sc[z])];
                        dp2[i][j] = max(dp2[i][j], (3 - tmp1 - tmp2 - tmp3) / 3.0);
                    }
                }
            }
        }
    }
    
    int main() {
        init();
        int n;
        while (n = readint()) {
            if (!n) break;
            printf("%.10f %.10f
    ", dp1[n][n], dp2[n][n]);
        }
    }
    
  • 相关阅读:
    Android 程序员不得不收藏的个人博客(持续更新...)
    硬核讲解 Jetpack 之 LifeCycle 源码篇
    秉心说,不一样的 2019
    秉心说 2019 博文合集
    庖丁解牛 Activity 启动流程
    Jetpack Compse 实战 —— 全新的开发体验
    Box 黑科技 —— 支持手机端反编译 !
    “无处不在” 的系统核心服务 —— ActivityManagerService 启动流程解析
    如何正确的在 Android 上使用协程 ?
    【Medium 万赞好文】ViewModel 和 LIveData:模式 + 反模式
  • 原文地址:https://www.cnblogs.com/hznumqf/p/13547355.html
Copyright © 2011-2022 走看看