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;
}

 

 

 

转载于:https://www.cnblogs.com/kedebug/archive/2012/12/05/2804091.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值