题目描述:
对于给出的n个物品,每个物品有一个价格p[i],你有m元钱,求最多能买的物品个数,以及有多少种不同的方案
题目分析:
类似01背包的题目,一般的01背包问题我们遇到的是求n个物品,有m的容量,每个有w[i]的花费,求出容量范围内的价值的最大值,动态转移方程为dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + value[i]),dp[i][j]存放前i种物品,容量为j时价值的最大值,优化空间之后是dp[j] = max(dp[j], dp[j-w[i]] + value[i]),而用此题去类比01背包的题目的语境,此时把每次纪念品的价格都当做是1,我们需要求的是给定的容量m的最大价值的搭配方法
原来就只需要求最大价值是多少,相当于多了一个维度存放搭配的数目,每一个i对应着一个二维数组,当更新当前第i个二维数组时,是根据第i-1个二维数组里的值更新的,所以此时需要用到三维数组存放,优化空间后可以用二维数组,且第2,3层循环都需要从大到小,避免干扰
套用01背包对动态转移方程进行推导:优化之前,dp[i][j][k]表示前i件物品,当有j元钱(不超过j元钱),可以购买k个物品的种数,dp[i][j][k] = dp[i-1][j][k] + dp[i-1][j-p[i]][k-1],(前i个物品的有j元,买k个物品的种数为前i-1个物品j元买k个(第i个不买)或者i-1个物品j-p[i]元买k-1件物品的种数(第i个买)),优化空间后为dp[j][k] = dp[j][k] + dp[j-p[i]][k-1]
需要注意的是初始化dp[i][0]都为1,意思是i元钱买0种物品的选择方案有1种就是不买
代码:
1 #include<iostream> 2 #include<string.h> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cmath> 6 using namespace std; 7 8 int p[35]; 9 int dp[505][35]; 10 11 int main(){ 12 int t; 13 scanf("%d", &t); 14 while(t--){ 15 int n, m; 16 scanf("%d%d", &n, &m); 17 for(int i = 1; i <= n; i++) scanf("%d", &p[i]); 18 sort(p+1, p+n+1); 19 int sum = 0; 20 int ma = 0; 21 for(int i = 1; i <= n; i++){ 22 if(sum + p[i] <= m){ 23 sum += p[i]; 24 ma = i; 25 } 26 } 27 memset(dp, 0, sizeof(dp)); 28 for(int i = 0; i <= m; i++) dp[i][0] = 1; 29 for(int i = 1; i <= n; i++){ 30 for(int j = m; j >= p[i]; j--){ 31 for(int k = ma; k >= 1; k--){ 32 dp[j][k] += dp[j-p[i]][k-1]; 33 } 34 } 35 } 36 if(ma != 0){ 37 printf("You have %d selection(s) to buy with %d kind(s) of souvenirs. ", dp[m][ma], ma); 38 }else{ 39 printf("Sorry, you can't buy anything. "); 40 } 41 } 42 return 0; 43 }