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
时间: 2024-11-01 12:25:52