美文网首页
[C++]面试题之动态规划类问题

[C++]面试题之动态规划类问题

作者: 循环不计次 | 来源:发表于2019-10-19 02:34 被阅读0次
    • 问题问法:
      Q:现在有4种硬币,面值分别为1、2、5、10分,如果要组成一毛3可以有多少种组法?
      Q:一个人上台阶的时候一次可以跨1、2、5级台阶,如果台阶有13级他有多少种走法?
      实质都是求用n种数字组合成数值m

    • 烧脑预警:
      因为我表达不太好,大家可能看的不是很懂,可以再看看
      https://blog.csdn.net/u013074465/article/details/48575585
      虽然我看这个文章时候也花了点时间想一些细节,毕竟这个问题真的三言两语讲不清楚
      如果往下的时候看不懂,回到下一行来看看这段话:
      ===========================================================================
      题目的问题实际上就是求dp[m][sum],即用前m种硬币(所有硬币)构成sum的所有组合数。
      设最后一种硬币的数量为xn。
      当xn=0时,有多少种组合呢? 实际上就是前i-1种硬币组合sum,有dp[i-1][sum]种!
      xn = 1 时呢,有多少种组合? 实际上是用前i-1种硬币组合成(sum - Vm)的组合数,有dp[i-1][sum -Vm]种;
      xn = 2 呢, dp[i-1][sum - 2 * Vm]种,等等。
      所有的这些情况加起来就是我们要求的dp[i][sum]。
      ===========================================================================

    • 动态规划的概念:
      将一个多阶段问题转化成一系列的单阶段问题

    • 转化讲解:
      用上面的问题做例子,用4种数字相加成目标值,多阶段的解法无非就是暴力枚举多层for循环镶嵌,
      这样很复杂也不是个聪明方法,但是把这一层一层for简化一下,转换一下思路
      比如你要求用2种数字组合成一个数值13(假设数字为1和2),这里把用i种数值的组合成数值j的情况设为数组dp[i][j];
      那么其实就是
      dp[2][13]=dp[1][13-0x2]+dp[1][13-1x2]+dp[1][13-2x2]+...+dp[1][13-6x2];
      为什么是这样呢,我来解释下里面的意思,dp[1][13-0x2]这个就是当数字2的个数为0的时候的组合数,
      那么dp[1][13-1x2]就是当数字2的数量为1的时候的组合数,为什么到6就停了呢?因为用2去组合的话最多
      只能用6个2,7个的话2x7=14已经超过目标数值了,这里就是把多个阶段给分解成了一个阶段,也就将
      求法隐式地转成了利用数字2个数组合地可能性求答案。
      简化了一层for的求值:

        int target=13;
        int last_coin=2;
        for(int k=0;k<=target/last_coin;++k)
        {
            dp[last_coin][target]+=dp[last_coin-1][target-k*last_coin];
        }
    

    看完代码就有人会问,那d[1][target-k*last_coin]的值从哪里来?那当然是根据d[0][target-k * last_coin]的值累积来的,那么d[0][target-k * last_coin]的值又从哪里来?朋友们,0的时候是在求问题“用0种硬币求目标数值可能性有多少种”的答案了,答案当然是1,你要用0种硬币求一个数值,那么所有的组合只有一种,数值是0,各种硬币的数量也为0.

    • 代码
    #include<iostream>
    using namespace std;
    int main(){
        int coins[4]={1,2,5,10};//硬币种类数组
        const int coin_kinds_num=sizeof(coins)/sizeof(coins[0]);
        const int total=13;//要组合成的目标数值
        /*以上为条件:硬币种类和要组合成的目标数值*/
        int dp[coin_kinds_num][total+1];
        for(int i=0;i<coin_kinds_num;i++)
        {
            dp[i][0]=1;
        }
        /*以上为初始化特殊情况(后面计算要用)当要组合成的目标数值为0的时候,只有一种情况,那就是各种硬币的搭配都是0个*/
        for(int i=1;i<=coin_kinds_num;++i)//这里i从1开始,因为已经跳过了特殊情况,而且也不会组合目标数值0
        {
            for(int j=1;j<=total;++j)//
            {
                dp[i][j]=0;//数组未初始化的时候值是随机数,这里初始化一下
                for(int k=0;k<=j/coins[i-1];++k)//这里的j/coins[i-1]是指用i种硬币去组合目标时,最多可以放多少个coins[i-1]这种硬币
                {
                    dp[i][j]+=dp[i-1][j-k*coins[i-1]];
                }
            }
        }
        return 0;
    }
    
    

    相关文章

      网友评论

          本文标题:[C++]面试题之动态规划类问题

          本文链接:https://www.haomeiwen.com/subject/svkgmctx.html