力扣-322-零钱兑换
一眼和昨天做的,一个梳子最少能用几个完全平方数凑成不是神似?
不过这里不一样的是,这里真的是背包问题,完全背包
瞄一眼确实是凑完全平方数那种方法,其实不太理解哪里能算是背包了
有个麻烦点是怎么返回-1的情况
自己都不确定对居然能通过了
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1,amount+1);
dp[0] = 0;
sort(coins.begin(), coins.end());
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.size(); j++) {
if (i == coins[j]) dp[i] = 1;
else if(i>coins[j]) dp[i] = min(dp[i],dp[i - coins[j]] + 1);
}
}
return dp[amount]>amount?-1:dp[amount];
}
来分析一下这代码为什么对,以及做一个空间优化
- 首先,虽然递推过程更像是一个递归过程,但是我们要用动态规划自底向上避免重复计算造成的超时问题
也就是第一层循环 - 其次,对于等于数组中元素的数直接dp[i]=1,这个没什么说的,下面递归过程取最小的过程其实也没什么说的
- 关键是凑不成的情况怎么处理?
举例:拿5,13去凑3,7
- 对于小于coins中最小的元素,肯定凑不成的,这也是为什么我要对coins排一遍,还想着直接return -1这肯定是不对的,那怎么让跟其他结果比较的时候知道这条路径不可取呢?
我们取的是最小值,那我们就把它置大,让它绝对不会作为合理的结果被选中
值多大呢?在哪里置?
初始化置,置一个绝对不会是正确答案的最小值,amount+1,就算全拿1来凑也最大amount,当然还有一种意外情况,就是amount=0,所以这里单独初始化 - 对于f(3)=f(1)+1,会被转化,所以还是只要考虑小于最小的coin的元素就行
这里的sort是多余的吗?找出最小的coin大小,不完全多余,是可以先处理比最小coin小的元素
但是没必要,因为首先这多了一步sort,带来额外的时间复杂度,另外不单独处理其实也行,用前面说的初始化置amount+1
然后考虑空间优化的问题…感觉不好空间优化啊,这又不是线性的
好,看一眼刷题笔记,确实这个dp数组是不能优化的,但是有一句语句是不不必要的
if (i == coins[j]) dp[i] = 1;
这里的情况能被下面的语句包含,如果i==coin[j]
的话,dp[i-coin[j]]==dp[0]=0+1=1
然后这里的也是不必要的,没有单独使用到j的情况,用增强for的话能省一个变量同时迭代器效率更高
最简洁的样子就是这样:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++)
for (int coin : coins)
if (i >= coin) dp[i] = min(dp[i], dp[i - coin] + 1);
return dp[amount] > amount ? -1 : dp[amount];
}