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

 

posted on 2017-09-21 23:18  目标120  阅读(776)  评论(0编辑  收藏  举报