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;

}

 

 

 

你可能感兴趣的:(poj)