【爬楼梯】
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
思路:
【状态】
dp[i];//爬i级台阶有几种方法
【初始】
dp[0] = 1;//爬0级1种(不爬) dp[1] = 1;//爬1级1种
【递推】
dp[i] = dp[i-2] + dp[i-1];//爬i级=先爬i-1级再爬1级+先爬i-2级再爬2级,没有其他可能了
【结论】
dp[n];//爬n级方法数
class Solution {
public:
int climbStairs(int n) {
vector dp(n + 1);
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
};
【杨辉三角】
给定一个非负整数 numRows
,生成「杨辉三角」的前 numRows
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
思路:
观察示例可以发现:
每一行的行号也是该行元素的最大下标;
每行的首尾元素是1;
其余元素是它左上和右上元素的和;
把以上规则翻译成代码语言即可。
class Solution {
public:
vector> generate(int numRows) {
vector> ans;
vector lastRow(1,1);
ans.push_back(lastRow);
if (numRows == 1)
return ans;
for (int i = 1; i < numRows; i++) {
vector curRow;
//当前行最大下标就是i
curRow.push_back(1);
for (int j = 1; j < i; j++) {
curRow.push_back(lastRow[j - 1] + lastRow[j]);
}
curRow.push_back(1);
ans.push_back(curRow);
lastRow.resize(i+1);
lastRow = curRow;
}
return ans;
}
};
【打家劫舍】
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
思路:
建立dp数组,dp[i]代表到房子i为止最高可以偷到的金额。
对当前房子,我们分别计算偷它和不偷它可以获得的最大金额,并记录两者中较大的那个:
偷它,那么i-1号房子不可以偷,总金额是,偷i-2及以前的房子+偷i;
不偷它,那么i-1房子可以偷,总金额就是偷i-1及以前的房子;
class Solution {
public:
int rob(vector& nums) {
int n = nums.size();
if (n == 1)
return nums[0];
vector dp(n, 0);
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < n; i++) {
dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]);
}
return dp[n - 1];
}
};
【完全平方数】
给你一个整数 n
,返回 和为 n
的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1
、4
、9
和 16
都是完全平方数,而 3
和 11
不是。
思路:
本题从如何减小问题规模入手。
首先,对拿到的数,我们已经知道任意一个比它小的数最少由几个完全平方数组成;
因此,我们可以先从当前数字中减掉一个完全平方数,然后通过dp直接拿到剩下的数的dp值,此时,这种切分方式得到的答案就是该dp值+1;
对当前数字,能从中减掉的完全平方数可能不止一个,因此,我们应该把能减的平方数全都试一遍,并把所有答案中最小的那个设为当前数字的dp值。
class Solution {
public:
int numSquares(int n) {
vector dp(n + 1);
dp[0] = 0;
//填数组
for (int k = 1; k <= n; k++) {
int curMin = INT_MAX;
//枚举平方根以下的数
for (int i = 1; i * i <= k; i++) {
curMin = min(curMin, dp[k - i * i]);
}
dp[k] = curMin + 1;
}
return dp[n];
}
};
【零钱兑换】
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
思路:
和上一题本质上没有任何区别,只要把上一题枚举的“完全平方数”在这题改成“硬币面值”,就完事儿了。
class Solution {
public:
int coinChange(vector& coins, int amount) {
int n = coins.size();
sort(coins.begin(), coins.end());
if (amount == 0)
return 0;
if (amount < coins[0])
return -1;
vector dp(amount + 1);
dp[0] = 0;
//依次计算
for (int i = 1; i <= amount; i++) {
int curMin = INT_MAX;
//记录当前金额能否凑成
bool flag = false;
// calculate dp[i]
//枚举比总数小的所有硬币面值
for (int j = 0; j < n && 0 <= i - coins[j]; j++) {
//如果去掉当前面值以后能凑出来
if (dp[i - coins[j]] != -1) {
//记录最少数量
curMin = min(curMin, dp[i - coins[j]] + 1);
//记录可以凑出
flag = true;
}
}
dp[i] = flag ? curMin : -1;
}
return dp[amount];
}
};