• POJ 1243 One Person(经典DP)


    题意:

    一个人去猜一个正整数,有G次机会,L个生命值。

    每一次猜数,如果猜对,就成功。猜错的话,机会减1,若猜的数大于目标数,另外生命值再减1。机会用完,或者生命值减至-1,表示失败。

    现给定G与L,问目标数在什么范围之内,可保证猜的人会胜利。

    思路:

    做这一题的时候不能被N这个数所迷惑,要用范围的概念去理解。

    dp[i][j]表示i次机会,j个生命值,可以覆盖的数值范围

    1. 如果j >= i,显然如果失去j个生命值都无法获取正确的数的话,i是没有意义的,所以j = i-1

    2. 如果j < i,我们要用最优的策略去走,显然是二分搜索了。下面我们用一张数轴来看待,这个数轴上面不存在所谓的原点。

       a. 对于dp[i][j],如果所在的点恰好是目标数,假设此时覆盖了点k

       b. 如果所在的点,比目标数要大,则此时的状态变成了dp[i-1][j-1], 相当于点k左边可以多覆盖dp[i-1][j-1]个点

       c. 如果所在的点,比目标数要小,则此时的状态变成了dp[i-1][j], 相当于点k右边可以多覆盖dp[i-1][j]个点

    所以总共可以覆盖的目标范围就是:dp[i-1][j-1] + 1 + dp[i-1][j]

    ps: 看了题解才恍然大悟,对于其中的思想叹为观止,动态规划的精妙之处莫过于此啊

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    int dp[32][32];
    
    int solve(int i, int j)
    {
        if (dp[i][j] != -1)
            return dp[i][j];
    
        if (j >= i)
            j = i - 1;
    
        dp[i][j] = solve(i-1, j-1) + 1 + solve(i-1, j);
        return dp[i][j];
    }
    
    int main()
    {
        int G, L;
        int cases = 0;
        while (scanf("%d %d", &G, &L) && G)
        {
            memset(dp, -1, sizeof(dp));
    
            for (int i = 0; i <= G; ++i)
                dp[i][0] = i;
            
            if (L >= G)
                L = G - 1;
            solve(G, L);
            
            printf("Case %d: %d\n", ++cases, dp[G][L]);
        }
        return 0;
    }
    -------------------------------------------------------

    kedebug

    Department of Computer Science and Engineering,

    Shanghai Jiao Tong University

    E-mail: kedebug0@gmail.com

    GitHub: http://github.com/kedebug

    -------------------------------------------------------

  • 相关阅读:
    《网络攻防》实验八:Web基础
    《网络攻防》实验七:网络欺诈技术防范
    《网络攻防》实验六:信息搜集与漏洞扫描
    《网络攻防》实验五:MSF基础应用
    《网络攻防》实验四:恶意代码分析
    《网络攻防》实验三:免杀原理与实践
    《网络攻防》实验二:后门原理与实践
    20145213《网络对抗》逆向及Bof基础
    《课程设计》——cupp的使用
    《课程设计》——foremost的使用
  • 原文地址:https://www.cnblogs.com/kedebug/p/2804091.html
Copyright © 2020-2023  润新知