目录
一.零钱兑换I
1.对应letecode链接:
2.题目描述:
3.解题思路:
1.这是一个非常经典的背包问题。对应背包问题每个位置只有选择和不选择的两种状态,并且当前位置做的选择不影响后续的选择。
2.在这里我门可以这样进行尝试,既然每种面值的硬币有无数妹那么我们可以尝试0枚,1枚,2枚但是枚数*对应的面值不能超过amout,我们每个位置都这样干那么答案就在其中。
下面我们写一下对应暴力的尝试:
//数组arr[index.........]凑出target的最少硬币数 int process(vector<int>& arr, int index, int target) { if (index == arr.size()) { return target == 0 ? 0 : INT_MAX; } //普遍位置 //[1,2,5] int ans = INT_MAX; //每个位置尝试0个,1个.....i个但是不能超过target for (int i = 0; i * arr[index] <= target; i++) { //后续搞定target-i*arr[index]所需要的硬币数量 int next = process(arr, index + 1, target - arr[index] * i); //如果真的是能够搞定那么更新答案 if (next != INT_MAX) { ans = min(ans, next + i); } } return ans; } };
2.对应记忆化搜索
class Solution { public: vector<vector<int>>dp; int coinChange(vector<int>& coins, int amount) { int N = coins.size(); dp.resize(N + 1, vector<int>(amount + 1, -1)); int ans = process(coins, 0, amount); return ans == INT_MAX ? -1 : ans; } //数组arr[index.........]凑出target的最少硬币数 int process(vector<int>& arr, int index, int target) { if (index == arr.size()) { return target == 0 ? 0 : INT_MAX; } //普遍位置 //[1,2,5] if (dp[index][target] != -1) { //说明之前已经计算过了 return dp[index][target]; } int ans = INT_MAX; //每个位置尝试0个,1个.....i个但是不能超过target for (int i = 0; i * arr[index] <= target; i++) { //后续搞定target-i*arr[index]所需要的硬币数量 int next = process(arr, index + 1, target - arr[index] * i); //如果真的是能够搞定那么更新答案 if (next != INT_MAX) { ans = min(ans, next + i); } } dp[index][target] = ans; return ans; } };
3.严格位置依赖的版本其实就是将暴力递归进行改写而已:
class Solution { public: // vector<vector<int>>dp; int coinChange(vector<int>& coins, int amount) { int N = coins.size(); vector<vector<int>>dp(N + 1, vector<int>(amount + 1)); dp[N][0] = 0; for (int i = 1; i <= amount; i++) { dp[N][i] = INT_MAX; } for (int index = N - 1; index >= 0; index--) { for (int target = 0; target <= amount; target++) { int ans = INT_MAX;//根据暴力递归进行改写 for (int i = 0; i * coins[index] <= target; i++) { int next = dp[index + 1][target - i * coins[index]]; if (next != INT_MAX) { ans = min(ans, next + i); } } dp[index][target] = ans; } } return dp[0][amount] == INT_MAX ? -1 : dp[0][amount]; } //数组arr[index.........]凑出target的最少硬币数 int process(vector<int>& arr, int index, int target) { if (index == arr.size()) { return target == 0 ? 0 : INT_MAX; } // if(dp[index][target]!=-1) // { // return dp[index][target]; // } //普遍位置 //[1,2,5] int ans = INT_MAX; for (int i = 0; i * arr[index] <= target; i++) { int next = process(arr, index + 1, target - arr[index] * i); if (next != INT_MAX) { ans = min(ans, next + i); } } // dp[index][target]=ans; return ans; } };
4.斜率优化:
1.我们发现在我们的动态规划当中出现了枚举行为我们可以尝试进行斜率优化我们先观察普遍位置:dp[index][target]它依赖的位置有:dp[index+1][target]、dp[index+1][target-arr[index]+1、dp[index+1][target-2*arr[index]]+2、dp[index+1][target-3*arr[index]]+3......
我们在来看dp[index][target-arr[index]]这个位置他依赖的位置又有那些了:
dp[index+1][target-arr[index]]+0、dp[index+1][target-2*arr[index]]+1、dp[index+1][target-3*arr[index]]+2..........
2.我们不难发现dp[index][target]=min(dp[index][target-arr[index]]+1,dp[index+1][target]。
对应优化代码:
class Solution { public: int coinChange(vector<int>& coins, int amount) { int N = coins.size(); vector<vector<int>>dp(N + 1, vector<int>(amount + 1)); for (int i = 0; i <= amount; i++) { dp[N][i] = INT_MAX; } dp[N][0] = 0; for (int index = N - 1; index >= 0; index--) { for (int target = 0; target <= amount; target++) { dp[index][target] = dp[index + 1][target]; if (target - coins[index] >= 0 && dp[index][target - coins[index]] != INT_MAX) { dp[index][target] = min(dp[index][target],dp[index][target - coins[index]] + 1); } } } return dp[0][amount] == INT_MAX ? -1 : dp[0][amount]; } };
二.零钱兑换II
1.对应letecode链接
2.题目描述:
3.解题思路:
本题解题思路和上题完全一模一样,只是函数的返回值不一样而已。
4.对应暴力递归代码:
class Solution { public: int change(int amount, vector<int>& coins) { return process(coins, 0, amount); } int process(vector<int>& arr, int index, int target) { //没数了 if (index == arr.size()) { return target == 0 ? 1 : 0; } // int ways = 0; //尝试每个位置旋i枚硬币 for (int i = 0; i * arr[index] <= target; i++) { ways += process(arr, index + 1, target - i * arr[index]); } return ways; } };
记忆化搜索代码:
class Solution { public: vector<vector<int>>dp; int change(int amount, vector<int>& coins) { dp.resize(coins.size() + 1, vector<int>(amount + 1, -1)); return process(coins, 0, amount); } int process(vector<int>& arr, int index, int target) { if (index == arr.size()) { return target == 0 ? 1 : 0; } if (dp[index][target] != -1) { return dp[index][target]; } // int ways = 0; for (int i = 0; i * arr[index] <= target; i++) { ways += process(arr, index + 1, target - i * arr[index]); } dp[index][target] = ways; return ways; } };
斜率优化代码:
class Solution { public: vector<vector<int>>dp; int change(int amount, vector<int>& coins) { dp.resize(coins.size() + 1, vector<int>(amount + 1, -1)); return process(coins, 0, amount); } int process(vector<int>& arr, int index, int target) { if (index == arr.size()) { return target == 0 ? 1 : 0; } if (dp[index][target] != -1) { return dp[index][target]; } //p int ways = process(arr, index + 1, target); if (target - arr[index] >= 0) { ways += process(arr, index, target - arr[index]); } dp[index][target] = ways; return ways; } };