zoukankan      html  css  js  c++  java
  • 换钱方法数(有限制条件和无限制条件)

    1.  先说一下有限制的

    /*
    给定一套货币(面值不同的一组货币) vector<int> moeys,以及每种货币的数量vector<int> cnts,求出组合成目标tar的方法数
    
    动态规划思想: dp[i][j]表示用货币moneys[0~i] 组合成 tar 的方法数
    dp的规模是:dp[moneys.size()][tar+1]
    
    <1> 初始化第一行,dp[0][j], 用moneys[0] 组成j的方法数, moneys[0]只能组成其整数倍的钱数
    <2> 初始化第一列, dp[i][0], 用任何面值组成0的方法都是1,即都不用,所以第一列是全1
    <3> 其他位置:dp[i][j]有以下几种可能
        <3.1> 不用moneys[i], 只用moneys[0~i-1]组成j,此时方法数是dp[i-1][j]
        <3.2> 用一张moneys[i], 且用moneys[0~i-1]组成j-1*moneys[i],此时方法数是dp[i-1][j-1*moneys[i]]
        <3.3> 用两张moneys[i], 且用moneys[0~i-1]组成j-2*moneys[i],此时方法数是dp[i-1][j-2*moneys[i]]
        <3.4> 用三张moneys[i], 且用moneys[0~i-1]组成j-3*moneys[i],此时方法数是dp[i-1][j-3*moneys[i]]
        .
        .
        .
        直到 当前货币张数用完,或者j-k*moneys[i]<0 则停止
        那么dp[i][j] 就是上面所有可能的对应的方法数的和。
      dp[i][j] = sum{dp[i-1][j-k*moneys[i]], 其中k=0,1,2,3...cnts[i] ,且j-k*moneys[i]}
    */ #include <bits/stdc++.h> using namespace std; int getChangeWays(vector<int> &moneys, vector<int> &cnts, int tar) { int len1=moneys.size(), len2=cnts.size(); int dp[len1][tar+1]; // 初始化第一列 for (int i=0; i<len1; i++) dp[i][0] = 1; /* 初始化第一行,注意里面的细节: <1> 对于小于moneys[0]的j,其值是0,(如:5元纸币是无法组成1~4元的) <2> 只有moneys[0]的整数倍,且不成超过张数限制的位置, dp[0][j] 才是1.(如:假如有4张5元纸币,那么能组成的面值 有5 10 15 20, 但是 25 30 .. 是5的整数倍但是由于张数不够,所以无法组成) */ for (int j=1; j<=tar; j++) { if (j>=moneys[0] && j%moneys[0]==0 && (j/moneys[0])<=cnts[0]) dp[0][j] = 1; else dp[0][j] = 0; } // 其他位置dp[i][j] for (int i=1; i<len1; i++) { for (int j=1; j<=tar; j++) { int ways = 0; /* 用k张当前货币,所对应的方法数是dp[i-1][j-k*moneys[i]]. 这里要保证0<=k<=cnts[i], 且 j-k*moneys[i]>=0 */ for (int k=0; k<=cnts[i] && (j-k*moneys[i])>=0; k++) { ways += dp[i-1][j-k*moneys[i]]; } dp[i][j] = ways; } } return dp[len1-1][tar]; } int main() { int tar; int arr[] = {1,5,10,20,50,100}; // 给出货币面值 vector<int> moneys(arr,arr+6); cin>>tar; vector<int> cnts(6,0); // 每种货币的数量 for (int i=0; i<6; i++) { cin>>cnts[i]; } cout<<getChangeWays(moneys, cnts, tar)<<endl; return 0; }

    2.  如果上面的,理解了的话,就进行无限制的情况

    无限制和有限制的区别,主要体现在两处:

    <2.1> 初始化第一列的时候, 没有了数量上的限制  

    <2.2> 求其他位置dp[i][j] 的时候,约束条件少了一条,并且可以减少一部分运算

    /*
    状态转化方程是:
    dp[i][j] = sum{dp[i-1][j-k*moneys[i]], 其中k=0,1,2,3... 且j-k*moneys[i]>=0} 换个形式 dp[i][j] = dp[i-1][j] + sum{dp[i-1][j-k*moneys[i]], 其中k=1,2,3... 且j-k*moneys[i]>=0} // 把第一项单独拿出来 = dp[i-1][j] + sum{dp[i-1][j-moneys[i]-k*moneys[i]], 其中k=0,1,2,3... 且j-moneys[i]-k*moneys[i]>=0} = dp[i-1][j] + dp[i][j-moneys[i]] */
      

    这时候的函数是下面的这个

    int getChangeWays2(vector<int> &moneys, int tar)
    {
        int len1=moneys.size(), len2=cnts.size();
        int dp[len1][tar+1];
        // 初始化第一列和第一列
    	for (int i=0; i<len1; i++) dp[i][0] = 1;
    	for (int j=1; j<=tar; j++)
        {
            if (j>=moneys[0] && j%moneys[0]==0) dp[0][j] = 1;
            else dp[0][j] = 0;
        }
    	
    	// 其他位置dp[i][j]
        for (int i=1; i<len1; i++)
        {
            for (int j=1; j<=tar; j++)
            {
                dp[i][j] = dp[i-1][j];
    			if (j-moneys[i]>=0) dp[i][j] += dp[i][j-moneys[i]];
            }
        }
        return dp[len1-1][tar];
    }  

    如果有写错的地方,还请各位大神指出!!!

    参考:

    左程云 《程序员代码面试指南》

    http://blog.csdn.net/qq_22222499/article/details/71249354

  • 相关阅读:
    第二部分:并发工具类17->ReadWriteLock:如何快速实现一个完备的缓存
    第二部分:并发工具类16->Semaphore:如何快速实现一个限流器
    第二部分:并发工具类15->Lock和condition(下)
    一款类似B站的开源弹幕播放器,太酷了
    2021年基础知识点复习
    Autofac.Core.DependencyResolutionException: An exception was thrown while activating Castle.Proxies.MiniProgramAppServiceProxy.
    一张图解析FastAdmin中的弹出窗口的功能
    vue 关闭代码严格模式,轻松运行
    vue项目严格模式下的常见错误
    mysql下载安装包及安装步骤
  • 原文地址:https://www.cnblogs.com/zyq-blog/p/7572027.html
Copyright © 2011-2022 走看看