第一个小目标:50道动态规划题目。
先把递推式写出来。 然后把初始条件写出来。
使用prev进行返回可能存在越界风险。使用cur进行返回需要在程序最开始时判断条件并返回。
需要注意的是起始条件,cur和n的对齐。即满足cur的初始值不进行计算的n应该是多少。
最大值或者最优解的重要思路就是选或者不选。
0. 阶段
第一阶段是连续递推,第二阶段是非连续的。
非连续怎么处理???
1. 509 斐波那契数
本题的核心在于两个变量(prev和cur)逐步往前走。
class Solution {
public:
int fib(int n) {
int prev = 0, cur = 1;
while (n--) {
int temp = cur;
cur = cur + prev;
prev = temp;
}
return prev;
}
};
class Solution {
public:
int fib(int n) {
if (n <= 1) return n;
int prev = 0, cur = 1;
n -= 1;
while (n--) {
int temp = cur;
cur = cur + prev;
prev = temp;
}
return cur;
}
};
class Solution {
public:
int fib(int n) {
if (n == 0) return 0;
int prev = 0, cur = 1;
while (--n) {
int temp = cur;
cur += prev;
prev = temp;
}
return cur;
}
};
2. 1137 第 N 个泰波那契数
需要注意的是用哪个变量作为返回值,其中该变量的范围很重要,比如返回值的最大值为 2 31 − 1 2^{31}-1 231−1,如果使用prev作为返回,则cur和prev使用int则会产生越界现象。
class Solution {
public:
int tribonacci(int n) {
unsigned int prev = 0, cur = 1, nex = 1;
while (n--) {
unsigned int t1 = cur;
unsigned int t2 = nex;
nex = prev + cur + nex;
prev = t1;
cur = t2;
}
return prev;
}
};
class Solution {
public:
int tribonacci(int n) {
if (n <= 1) return n;
int prev = 0, cur = 1, nex = 1;
n -= 2;
while (n--) {
int t1 = cur;
int t2 = nex;
nex += cur + prev;
cur = t2;
prev = t1;
}
return nex;
}
};
class Solution {
public:
int tribonacci(int n) {
if (n <= 1) return n;
int prev = 0, cur = 1, nex = 1;
--n;
while (--n) {
int t1 = cur;
int t2 = nex;
nex += (cur + prev);
cur = t2;
prev = t1;
}
return nex;
}
};
3. 70 爬楼梯
class Solution {
public:
int climbStairs(int n) {
if (n == 1) return 1;
int prev = 1, cur = 2;
n -= 2;
while (n--) {
int temp = cur;
cur += prev;
prev = temp;
}
return cur;
}
};
class Solution {
public:
int climbStairs(int n) {
if (n == 1) return 1;
int prev = 1, cur = 2;
--n;
while (--n) {
int temp = cur;
cur += prev;
prev = temp;
}
return cur;
}
};
确定零点很重要,这里的零点是2。
4. 746 使用最小花费爬楼梯
其中走到第N个台阶,是从第N-1或者N-2个台阶上再走第N个台阶。
f(0) = nums[0]
f(1) = nums[1]
f(i) = min(f(i-1), f(i-2)) + nums[i]
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
if (cost.size() == 2) return min(cost[0], cost[1]);
cost.push_back(0);
int prev = cost[0];
int cur = cost[1];
for (int i=2; i<cost.size(); ++i) {
int temp = cur;
cur = min(prev, cur) + cost[i];
prev = temp;
}
cost.pop_back();
return cur;
}
};
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int prev = cost[0], cur = cost[1];
cost.push_back(0);
for (int i=2; i<cost.size(); ++i) {
int temp = cur;
cur = min(prev, cur) + cost[i];
prev = temp;
}
cost.pop_back();
return cur;
}
};
爬楼梯和打家劫舍有很大的区别。前者和后者的区别之处在于:爬楼梯必须是从N-1或者N-2到第N个台阶(相邻性),而打家劫舍是非相邻性。还有个区别是前者必须要爬该层楼梯,而后者不一定要偷遍历到的这家。
5. 198 打家劫舍
f(0) = nums[0]
f(1) = max(f(0), f(1))
f(i) = max(f(i-1), f(i-2)+nums[i])
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 1) return nums[0];
int prev = nums[0];
int cur = max(nums[0], nums[1]);
for (int i=2; i<nums.size(); ++i) {
int temp = cur;
cur = max(prev+nums[i], cur);
prev = temp;
}
return cur;
}
};
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 1) return nums[0];
int prev = nums[0], cur = max(nums[0], nums[1]);
for (int i=2; i<nums.size(); ++i) {
int temp = cur;
cur = max(prev+nums[i], cur);
prev = temp;
}
return cur;
}
};
不能取相邻,本来原本是两个方向,为什么用单个方向来代替也是可以的~~~
6. 213 打家劫舍 I I II II
class Solution {
private:
// [start, end]
int rob(vector<int>& nums, int start, int end) {
if (start > end) return 0;
if (start == end) return nums[start];
int prev = nums[start], cur = max(nums[start], nums[start+1]);
for (int i=start+2; i<nums.size() && i<=end; ++i) {
int temp = cur;
cur = max(cur, prev+nums[i]);
prev = temp;
}
return cur;
}
public:
int rob(vector<int>& nums) {
if (nums.size() == 1) return nums[0];
return max(rob(nums, 0, nums.size()-2), rob(nums, 1, nums.size()-1));
}
};
7. 740 删除并获得点数
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
int maxNum = *max_element(nums.begin(), nums.end());
vector<int> counts(maxNum);
for (int num: nums) counts[num-1] += 1;
if (counts.size() == 1) return counts[0];
int prev = counts[0], cur = max(counts[0], 2*counts[1]);
for (int i=2; i<counts.size(); ++i) {
int temp = cur;
cur = max(prev+(i+1)*counts[i], cur);
prev = temp;
}
return cur;
}
};
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
int maxNum = *max_element(nums.begin(), nums.end());
vector<int> counts(maxNum, 0);
for (int num: nums) counts[num-1] += 1;
if (counts.size() == 1) return counts[0];
int prev = counts[0], cur = max(counts[0], 2*counts[1]);
for (int i=2; i<counts.size(); ++i) {
int temp = cur;
cur = max(cur, prev+counts[i]*(i+1));
prev = temp;
}
return cur;
}
};
真实值=下标+1
8. 55 跳跃游戏
class Solution {
public:
bool canJump(vector<int>& nums) {
int maxpos =0;
int n =nums.size();
for(int i=0;i<n;i++){
if(i>maxpos) return false;
maxpos = max(maxpos,i+nums[i]);
}
return true;
}
};
9. 5 最长回文子串
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n < 2) return s;
int maxLen = 1;
int begin = 0;
vector<vector<bool>> dp(n, vector<bool> (n));
for (int i=0; i<n; ++i) dp[i][i] = true;
for (int L=2; L<=n; ++L){
for (int i=0; i<n-1; ++i) {
int j = i + L - 1;
if (j >= n) break;
if (s[i] != s[j]) {
dp[i][j] = false;
} else{
if (j - i < 3) {
dp[i][j] = true;
} else{
dp[i][j] = dp[i+1][j-1];
}
}
if (dp[i][j] & maxLen < j-i+1){
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substr(begin, maxLen);
}
};
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n < 2) return s;
int maxLen = 1, begin = 0;
// [i..j]
vector<vector<bool>> dp(n, vector<bool>(n));
for (int i=0; i<n; ++i) dp[i][i] = true;
for (int L=2; L<=n; ++L) {
for (int i=0; i<n-1; ++i) {
int j = i + L - 1;
if (j >= n) break;
if (s[i] != s[j]) {
dp[i][j] = false;
} else {
if (L <= 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i+1][j-1];
}
if (dp[i][j] == true && L > maxLen) {
maxLen = L;
begin = i;
}
}
}
}
return s.substr(begin, maxLen);
}
};
需要特别注意的是最外层的循环变量为L,然后再是i。
class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
if (len < 2) return s;
int maxLen = 1, begin = 0;
vector<vector<bool>> dp(len, vector<bool> (len, false));
for (int i=0; i<len; ++i) dp[i][i] = true;
for (int L=2; L<=len; ++L) {
for (int i=0; i<len-1; ++i) {
int j = i + L - 1;
if (j >= len) break;
if (s[i] != s[j]) {
dp[i][j] = false;
} else{
if (L <= 3) dp[i][j] = true;
else {
if (dp[i+1][j-1]) {
dp[i][j] = true;
}
}
}
if (dp[i][j] && maxLen < L) {
maxLen = L;
begin = i;
}
}
}
return s.substr(begin, maxLen);
}
};
10. 1143 最长公共子序列
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size();
int n = text2.size();
int res = 0;
vector<vector<int>> dp(m+1, vector<int> (n+1, 0));
for (int i=0; i<m+1; ++i) dp[i][0] = 0;
for (int i=0; i<n+1; ++i) dp[0][i] = 0;
for (int i=0; i<m; ++i) {
for (int j=0; j<n; ++j) {
if (text1[i] == text2[j]) {
dp[i+1][j+1] = dp[i][j] + 1;
} else{
dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]);
}
}
}
return dp[m][n];
}
};
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size(), n = text2.size();
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for (int i=0; i<=m; ++i) dp[i][0] = 0;
for (int i=0; i<=n; ++i) dp[0][i] = 0;
for (int i=0; i<m; ++i) {
for (int j=0; j<n; ++j) {
if (text1[i] == text2[j]) {
dp[i+1][j+1] = dp[i][j] + 1;
} else{
dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]);
}
}
}
return dp[m][n];
}
};
11. 516 最长回文子序列
class Solution {
public:
int longestPalindromeSubseq(string s) {
string revS;
revS.assign(s.rbegin(),s.rend());
int n = s.size();
vector<vector<int>> dp(n+1, vector<int> (n+1, 0));
for (int i=0; i<n; ++i) {
for (int j=0; j<n; ++j) {
if (s[i] == revS[j]) {
dp[i+1][j+1] = dp[i][j] + 1;
} else{
dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]);
}
}
}
return dp[n][n];
}
};
12. 1014 最佳观光组合
class Solution {
public:
int maxScoreSightseeingPair(vector<int>& values) {
int res = values[0] + 0 + values[1] -1;
int preMax = values[0] + 0;
for (int i=1; i<values.size(); ++i) {
res = max(res, preMax + values[i] - i);
preMax = max(preMax, values[i] + i);
}
return res;
}
};
其中preMax相当于是values[i]+i中的最大值。如果不省略变量可能会更容易理解一些。
class Solution {
public:
int maxScoreSightseeingPair(vector<int>& values) {
int res = values[0] + 0 + values[1] -1;
int preMax = values[0] + 0;
for (int j=1; j<values.size(); ++j) {
int i = j;
res = max(res, preMax + values[j] - j);
preMax = max(preMax, values[i] + i);
}
return res;
}
};