力扣322. 零钱兑换(动态规划:最值型问题)
https://leetcode-cn.com/problems/coin-change/
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
动态规划
最值型问题
自下而上
复杂度分析
-
时间复杂度:O(Sn),其中 S 是金额,n 是面额数。我们一共需要计算 O(S) 个状态,S 为题目所给的总金额。对于每个状态,每次需要枚举 n 个面额来转移状态,所以一共需要 O(Sn) 的时间复杂度。
-
空间复杂度:DP 数组需要开长度为总金额 S 的空间。
1、确定状态
最后一步:
子问题
与递归的区别:递归存在很多重复计算,
动态规划就是用空间换时间,将计算结果保留下来,并改变计算顺序
2、转移方程
3、初始条件和边界情况
4、计算顺序
//
// main.cpp
// dynamicprogramming
//
// Created by MXQ on 2020/10/15.
//
#include <iostream>
#include<vector>
#include <algorithm>
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
using namespace std;
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int>f;
int result=0;
unsigned int n=coins.size();//number ofkinds of coins
//initialization
f.push_back(0);
//从1计算到amount,计算顺序从小到大
for (int i=1; i<=amount; i++) {
//初始化为正无穷,拼不出
f.push_back(INT_MAX);
//看有多少个硬币种类
for (int j=0; j<n; j++) {
//i-coins[j]不能是负数,f[i-coins[j]]==INT_MAX的话没有必要算,代表的情况是拼不出
if (i>=coins[j] && f[i-coins[j]]!=INT_MAX) {
if ((f[i-coins[j]]+1)<=(f[i])) {
f[i]=f[i-coins[j]]+1;
}
}
}
}
//题目说拼不出的话返回-1
if (f[amount]==INT_MAX) {
f[amount]=-1;
}
return f[amount];
}
};
int main(int argc, const char * argv[]) {
// insert code here...
Solution s;
vector<int> coins;
coins.push_back(1);
coins.push_back(2);
coins.push_back(5);
int amount=11;
auto result=s.coinChange(coins,amount);
std::cout << "how much: "<<result<<endl;
std::cout << "Hello, World!\n";
return 0;
}
动态规划解决的三类问题:
-
计数问题:多少种方式,多少种方法,how much
-
最大值最小值问题:max,min,最大最长
-
求存在性:取石子是否必胜,能不能选出k个数使和sum最大
C++最大值和最小值表示
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000