掘金团队号上线,助你 Offer 临门! 点击 查看详情
一、题目描述:
二、思路分析:
口袋n张钱,买价值m的玩具,明摆着背包了。走之前讲过的动态规划流程1,动归流程细分。
确定状态
状态表示
dp[i][j] : 第i张钱, 价值j
状态计算
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j - 5] + dp[i - 1][j - 10] + dp[i - 1][j - 50];
真的是这样吗?
以上的代码会造成 1 1 5 和 1 5 1重复出现的可能,会导致数据变得错误,因此换一种思路
思路二
状态表示
dp[i][j]: 第i种钱, 价值j arr = {1, 5, 10, 50} dp[0][j]: 仅使用1元纸币,价值j
这样的话就是{1,1,1,1,1} j个1组成的数组
dp[1][j]: 仅使用1元纸币,5元纸币。价值为j。
dp[1][j] = dp[0][j - 5] + dp[0][j - 10] + ...
这样的话就能避免思路1出现的结果重复。
最后循环一次dp dp[i][j].size() 如果小于 n 就ret++
三、AC 代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int buytoys(int n, int m){
// 共有n个票,1,5,10,50
int coins[] = {1, 5, 10, 50};
vector< vector< vector<int> > > arr;
for(int i = 0; i < m + 1; i++){
vector<vector<int> > tmp;
if(i == 0){
vector<int> asp;
tmp.push_back(asp);
}
arr.push_back(tmp);
}
//arr[0]
for(int i = 0; i < 4; i++){
for(int j = coins[i]; j < m + 1; j++){
if(arr[ j - coins[i]].size() != 0){
vector <vector<int> > tmp = arr[ j - coins[i]];
for(int k = 0; k < tmp.size(); k++){
tmp[k].push_back(coins[i]);
}
arr[j].insert(arr[j].end(),tmp.begin(), tmp.end());
}
}
}
int ret = 0;
int b = arr.size() - 1;
for(int i = 0; i < arr[b].size(); i++){
if(arr[b][i].size() <= n){
ret++;
}
}
return ret;
}
int main(){
//这个输出为4
// [50] 使用了1张
// [10] 使用了5张
// [10] 使用了4张 [5]使用了2张
// [10] 使用了3张 [5] 使用了4张
// 如果要求必须使用n张,而不是n张以下欸,则只有“[10] 使用了3张 [5] 使用了4张”满足条件
cout << buytoys(7, 50) <<endl;
}
四、总结:
相对于普通的背包,这道题以第几类钱为第一维还是有挑战性的,比较难想。不过上述代码并不是完全体,还是可以剪枝的。掘友可以自行更新。