70. Climbing Stairs
定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数
之和。
考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N)空间复杂度优化为 O(1) 复杂度。
class Solution {
public:
int climbStairs(int n) {
vector res(n+1, -1);
res[0] = 1;
res[1] = 1;
for (int i = 2; i <= n; ++i)
res[i] = res[i - 1] + res[i - 2];
return res[n];
}
};
class Solution2 {
public:
int climbStairs(int n) {
int dp_1 = 1;
int dp_2 = 1;
if (n == 0)
return dp_2;
if (n == 1)
return dp_1;
for (int i = 2; i <= n; ++i)
{
int temp = dp_1;
int dp_1 = dp_1 + dp_2;
dp_2 = temp;
}
return dp_1;
}
};
198. House Robber
定义 dp 数组用来存储最大的抢劫量,其中 dp[i] 表示抢到第 i 个住户时的最大抢劫量。由于不能抢劫邻近住户,如果抢劫了第 i -1 个住户,那么就不能再抢劫第 i 个住户,所以
class Solution {
public:
int rob(vector& nums) {
int dp_1 = 0; //表示上一次抢劫的
int dp_2 = 0; //表示上上次抢劫的
for (int i = 0; i < nums.size(); ++i)
{
int temp = dp_1;
dp_1 = max(dp_2 + nums[i], dp_1); //当前抢劫的是上一次抢劫的与上上次抢劫的加上本次抢劫的最大值
dp_2 = temp;
}
return dp_1;
}
};
213. House Robber II
class Solution {
public:
int rob(vector& nums) {
int n = nums.size();
if (n == 0)
return 0;
if (n == 1)
return nums[0];
return max(robCore(nums, 0, n - 2), robCore(nums, 1, n - 1));
}
private:
int robCore(vector& nums, int l, int r)
{
int dp_1 = 0;//前一个
int dp_2 = 0;//前前一个
int res = 0;
for (int i = l; i <= r; ++i)
{
int temp = dp_1;
dp_1 = max(dp_1, dp_2 + nums[i]);
dp_2 = temp;
}
return dp_1;
}
};
题目描述:
有 N 个信和信封,它们被打乱(也就是每封信都不在正确的信封中),求错误装信方式的数量。
解题思路:
定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况:
i==k,交换 i 和 k 的信后,它们的信和信封在正确的位置,但是其余 i-2 封信有 dp[i-2] 种错误装信的方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-2] 种错误装信方式。
i != k,交换 i 和 j 的信后,第 i 个信和信封在正确的位置,其余 i-1 封信有 dp[i-1] 种错误装信方式。由于 j 有 i-1种取值,因此共有 (i-1)*dp[i-1] 种错误装信方式。
综上所述,错误装信数量方式数量为:
这个思路好像更好理解:
当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用dp[n]表示,那么dp[n-1]就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.
第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;
第二步,放编号为k的元素,这时有两种情况:⑴把它放到位置n,那么,对于剩下的n-1个元素,由于第k个元素放到了位置n,剩下n-2个元素就有dp[n-2]种方法;⑵第k个元素不把它放到位置n,这时,对于这n-1个元素,有dp[n-1]种方法。
int lettersIncorrectlyArranged(int n)
{
vector dp(n+1, 0);
dp[1] = 0;
dp[2] = 1;
for (int i = 3; i <= n; ++i)
dp[i] = (i - 1)*(dp[i-2] + dp[i-1]);
return dp[n];
}
题目描述:
假设农场中成熟的母牛每年都生 1 头小母牛,并且永远不会死。第一年有 1 只小母牛,从第二年开始,母牛开始生小母牛。每只小母牛 3 年之后成熟又可以生小母牛。给定整数 N,求 N 年后牛的数量。
根据题目易得第i年的牛数量为
int cowProduction(int n)
{
vector count(n+1, 0);
count[1] = 1;
count[2] = 2;
count[3] = 3;
for (int i = 4; i <= n; ++i)
count[i] = count[i - 1] + count[i - 3];
return count[n];
}
64. Minimum Path Sum
class Solution {
public:
int minPathSum(vector>& grid) {
if (grid.size() == 0)
return 0;
int rows = grid.size();
int cols = grid[0].size();
for(int i = 0;i
62. Unique Paths
class Solution {
public:
int uniquePaths(int m, int n) {
vector> dp(m, vector(n, 1));
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
{
if (i == 0 || j == 0)
continue;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
return dp[m - 1][n - 1];
}
};
53. Maximum Subarray
一般解法
class Solution {
public:
int maxSubArray(vector& nums) {
int sum = INT_MIN;
int curSum = 0;
for (int i = 0; i < nums.size(); ++i)
{
curSum += nums[i];
sum = max(sum, curSum);
if (curSum < 0) //如果当前贡献值为负
curSum = 0;
}
return sum;
}
};
DP解法
DP[i]表示i之前并包含nums[i]的子数组的最大和,无非两种情况:DP[i] = DP[I-1] + nums[i]或者DP[i] = nums[i],因此dp[i] = max(dp[i-1] + nums[i], nums[i]),取DP[i]的最大值即可。
class Solution {
public:
int maxSubArray(vector& nums) {
int n = nums.size();
if (n == 0)
return 0;
int dp_i = nums[0];
int res = dp_i;
for (int i = 1; i < n; ++i)
{
int dp_pre = dp_i;
dp_i = max(dp_pre + nums[i], nums[i]);
res = max(res, dp_i);
}
return res;
}
};
413. Arithmetic Slices
dp[i] 表示以 A[i] 为结尾的等差递增子区间的个数。
在 A[i] - A[i - 1] == A[i - 1] - A[i - 2] 的条件下,{A[i - 2], A[i - 1], A[i]} 是一个等差递增子区间。如果 {A[i - 3], A[i - 2], A[i - 1]} 是一个等差递增子区间,那么 {A[i - 3], A[i - 2], A[i - 1], A[i]} 也是等差递增子区间,dp[i] = dp[i-1] + 1。
class Solution {
public:
int numberOfArithmeticSlices(vector& A) {
int n = A.size();
vector dp(n, 0);
//dp[i]表示以A[i]结尾的
for (int i = 2; i < n; ++i)
{
if (A[i] - A[i - 1] == A[i - 1] - A[i - 2])
dp[i] = dp[i - 1] + 1;
}
return accumulate(dp.begin(), dp.end(), 0);
}
};
343. Integer Break
class Solution {
public:
int integerBreak(int n) {
vector dp(n+1, 0);
dp[1] = 1;
for (int i = 2; i <= n; ++i)
{
for (int j = 1; j < i; ++j)
dp[i] = max(dp[i], max(j * dp[i-j], j*(i-j)));
}
return dp[n];
}
};
279. Perfect Squares
class Solution {
public:
int numSquares(int n) {
vector dp(n+1, INT_MAX);
dp[0] = 0; //注意初始值问题,要么dp全部初始化为INT_MAX,要么将dp[0]初始化为0
int cnt = 1;
for (int i = 1; i <= n; ++i)
{
if (cnt* cnt == i)
{
dp[i] = 1;
++cnt;
}
else
{
for (int j = 1; j < cnt; ++j)
dp[i] = min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
};
91. Decode Ways
class Solution {
public:
int numDecodings(string s) {
if (s.empty() || s[0] == '0')
return 0;
//dp[i]表示0~i的numdecodings
vector dp(s.length()+1, 0);
dp[0] = 1; //注意:一般这种情况都是需要特殊验证一下以确定dp[0]的初值
dp[1] = 1; //第0个decoding
for (int i = 2; i <= s.length(); ++i)
{
//第i-1个decoding
//第1个decoding
//判断当前是否为0,如果是0,则表示当前位不可以单独拿出来,dp[i] = 0;
//如果不是0,则表示当前位可以单独拿出来,dp[i] = dp[i-1]
dp[i] = s[i-1] == '0' ? 0 : dp[i - 1]; //注意下标问题,i-1表示当前位置
//判断与前一位是否匹配
if (s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))
dp[i] += dp[i - 2];
}
return dp[s.length()];
}
};
//由于dp[i]只与dp[i-1]和dp[i-2]有关,所以可以将空间复杂度降为O(1)
class Solution {
public:
int numDecodings(string s) {
int n = s.length();
if (n == 0 || s[0] == '0')
return 0;
int dp_i = 0;
int dp_i_1 = 1; //dp[i-1]
int dp_i_2 = 1; //dp[i-2]
if (n == 1)
return dp_i_1;
for (int i = 1; i < n; ++i)
{
dp_i = s[i] == '0' ? 0 : dp_i_1;
if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] <= '6'))
dp_i += dp_i_2;
dp_i_2 = dp_i_1;
dp_i_1 = dp_i;
}
return dp_i;
}
};
300. Longest Increasing Subsequence
class Solution {
public:
int lengthOfLIS(vector& nums) {
int n = nums.size();
if (n < 2)
return n;
int res = 1;
vector dp(n, 1); //dp[i]表示包含i之前的最长子串长度
//关于什么时候用dp(n+1)什么时候用dp(n)取决于dp[i]的值和前几个值的相关度
for (int i = 1; i < n; ++i)
{
for (int j = 0; j < i; ++j)
if (nums[j] < nums[i])
dp[i] = max(dp[i], dp[j] + 1);
res = max(res, dp[i]);
}
return res;
}
};
646. Maximum Length of Pair Chain
这个题跟上面的题思路类似,不过要先对整数对进行排序。
class Solution {
public:
int findLongestChain(vector>& pairs) {
int n = pairs.size();
if (n <2)
return n;
vector dp(n, 1); //dp[i]表示包含i的最长串长度
int res = 1;
sort(pairs.begin(), pairs.end(), cmp);
for (int i = 1; i < n; ++i)
{
for (int j = 0; j < i; ++j)
if (pairs[i][0] > pairs[j][1])
dp[i] = max(dp[i], dp[j] + 1);
res = max(res, dp[i]);
}
return res;
}
private:
static bool cmp(vector& lhs, vector& rhs)
{
if (lhs[1] == rhs[1])
return lhs[0] < rhs[0];
return lhs[1] < rhs[1];
}
};
376. Wiggle Subsequence
class Solution {
public:
int wiggleMaxLength(vector& nums) {
int up = 1;
int down = 1;
int n = nums.size();
if (n < 2)
return n;
for (int i = 1; i < n; ++i)
{
if (nums[i] > nums[i - 1])
up = down + 1;
else if (nums[i] < nums[i - 1])
down = up + 1;
}
return max(up, down);
}
};