剑指offer刷题动态规划
Posted 非晚非晚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer刷题动态规划相关的知识,希望对你有一定的参考价值。
记录在Leetcode刷《剑指offer》的笔记,希望提高自己的算法基础和编程水平。这一篇文章刷的是动态规划的题目集合,在CSDN做一下记录,随时更新,一起学习吧。
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
一般它的步骤分为下列四步:
- 状态定义
- 转移方程
- 初始化
- 返回值
刷题链接:https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/55c0wg/
1. 剑指 Offer 10- I. 斐波那契数列
- 题目
- 提交答案
思路:普通循环(递归都可以转化为循环),每次计算前两个元素的加法值即可。要注意提示的取余操作,因为最多要做100次,所以必须在for循环内取余。
f(n + 1) = f(n) + f(n - 1)为转移方程。
class Solution {
public:
int fib(int n) {
if(n ==0 || n == 1)return n;
int fib_1 = 0;
int fib_2 = 1;
int temp = 0;
for(int i = 1; i < n; i++)
{
temp = fib_2;
fib_2 += fib_1;
fib_2 %=1000000007;
fib_1 = temp;
}
return fib_2;
}
};
- 题目解析
状态转移,3个变量轮流转移值。
class Solution {
public:
int fib(int n) {
int a = 0, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
};
2. 剑指 Offer 10- II. 青蛙跳台阶问题
- 题目
- 题目解析
思路:也是斐波那契数列问题。设跳上 n级台阶有 f(n) 种跳法,n - 1级台阶有f(n-1)中跳法,所以n+1级台阶就是n和n-1级方法的和。
- 当跳1级台阶: 剩 n-1个台阶,此情况共有 f(n-1)种跳法
- 当跳2级台阶: 剩 n-2 个台阶,此情况共有 f(n-2) 种跳法。
- 即 f(n) 为以上两种情况之和,即 f(n)=f(n-1)+f(n-2)
- 提交答案
class Solution {
public:
int numWays(int n) {
if(n== 0 || n== 1)return 1;
int a = 1;
int b = 1;
int sum = 0;
for(int i = 2; i <=n; i++)
{
sum = (a + b)%1000000007;
a = b;
b = sum;
}
return sum;
3. 剑指 Offer 19. 正则表达式匹配
-
题目
-
题目解析
思路:
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size() + 1, n = p.size() + 1;
vector<vector<bool>> dp(m, vector<bool>(n, false));
dp[0][0] = true;
// 初始化首行
for(int j = 2; j < n; j += 2)
dp[0][j] = dp[0][j - 2] && p[j - 1] == '*';
// 状态转移
for(int i = 1; i < m; i++) {
for(int j = 1; j < n; j++) {
if(p[j - 1] == '*') {
if(dp[i][j - 2]) dp[i][j] = true; // 1.
else if(dp[i - 1][j] && s[i - 1] == p[j - 2]) dp[i][j] = true; // 2.
else if(dp[i - 1][j] && p[j - 2] == '.') dp[i][j] = true; // 3.
} else {
if(dp[i - 1][j - 1] && s[i - 1] == p[j - 1]) dp[i][j] = true; // 1.
else if(dp[i - 1][j - 1] && p[j - 1] == '.') dp[i][j] = true; // 2.
}
}
}
return dp[m - 1][n - 1];
}
};
4. 剑指 Offer 42. 连续子数组的最大和
- 题目
- 题目解析
思路:
- 状态定义:动态规则列表dp,dp[i]表示以元素num[i]结尾的连续子数组最大和。
- 转移方程:分dp[i - 1]是否大于0,对dp[i]是否做贡献。
- 初始状态:dp[0] = nums[0].
- 返回值:返回dp列表中的最大值。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res = nums[0];
for(int i = 1; i < nums.size(); i++) {
if(nums[i - 1] > 0) nums[i] += nums[i - 1];//num[i-1]是否有贡献
if(nums[i] > res) res = nums[i];//加入后的数据是否有贡献
}
return res;
}
};
5. 剑指 Offer 46. 把数字翻译成字符串
- 题目
- 提交结果
思路:从个位数开始,依次判断状态。根据上一个状态是单独翻译,还是组合翻译进行分类。
class Solution {
public:
int translateNum(int num) {
if( num < 10)
return 1;
int pair = 0;
int single = 1;//预算一个元素,从第二个元素开始
int n = num%10;//保留一个将要删除的元素
int temp;
while(num/10 != 0)
{
num = num / 10;//删除元素
if( (num % 10 > 2) || (num%10 == 0) || ((num % 10 == 2) && (n > 5))) {single = pair + single; pair = 0;}//必须独立翻译
else {temp = single; single = pair + single; pair = temp; }
n = num%10;//取余
}
return single + pair;
}
};
- 题目解析
思路:字符串方案
class Solution {
public:
int translateNum(int num) {
string s = to_string(num);
int a = 1, b = 1, len = s.size();
for(int i = 2; i <= len; i++) {
string tmp = s.substr(i - 2, 2);
int c = tmp.compare("10") >= 0 && tmp.compare("25") <= 0 ? a + b : a;
b = a;
a = c;
}
return a;
}
};
6. 剑指 Offer 47. 礼物的最大价值
- 题目
- 提交答案
思路:斜线开始依次遍历,不过看了题目解析,其实没必要。。。。
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
vector<vector<int>> s (m, vector<int>(n, 0));//m个向量
s[0][0] = grid[0][0];//第一个元素不用计算
if(m == 1 && n == 1)
return s[0][0];
int i = 0;
int j = 0;
for(int k = 1; k < (m + n - 1); k++)//每次增加一步
{
if(k > m - 1) {i = m -1;j = k - m + 1;}
else{ i = k;j = 0;}
for(; j <= k; i--,j++)
{
if(j >= n || i < 0) break;
if(i == 0 ) s[i][j] = s[i ][j - 1];//只有一种路径
else if(j == 0 ) s[i][j] = s[i - 1][j];
else {s[i][j] = max(s[i][j-1], s[i-1][j]);}//两种路径
s[i][j] += grid[i][j];
}
}
return s[m-1][n-1];
}
};
- 题目解析
思路,直接遍历,边上的直接累加,中间的比较之后累加。
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(i == 0 && j == 0) continue;
if(i == 0) grid[i][j] += grid[i][j - 1] ;
else if(j == 0) grid[i][j] += grid[i - 1][j];
else grid[i][j] += max(grid[i][j - 1], grid[i - 1][j]);
}
}
return grid[m - 1][n - 1];
}
};
7. 剑指 Offer 48. 最长不含重复字符的子字符串
- 题目
- 提交答案
思路:遍历字符串,按照是否有共同元素分类进行,需要注意的是,两个判断都需要用break。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.size();
if(len == 0 || len == 1)return len;
int begin = 0;
int end = 1;
int sum = 1;
while(end < len)
{
for(int i = begin; i < end; i++)
{
if(s[end] == s[i])//相同元素
{
sum = max(end - begin, sum);
begin = i + 1;
end++;
break;//跳出
}
else if(i == end - 1)//没有相同元素
{
end++;
break;
}
}
}
sum = max(end - begin, sum);
return sum;
}
};
- 题目解析
思路:哈斯表+线性遍历
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> dic;
int res = 0, tmp = 0, len = s.size(), i;
for(int j = 0; j < len; j++) {
if(dic.find(s[j]) == dic.end()) i = - 1;
else i = dic.find(s[j])->second; // 获取索引 i
dic[s[j]] = j; // 更新哈希表
tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
res = max(res, tmp); // max(dp[j - 1], dp[j])
}
return res;
}
};
8. 剑指 Offer 49. 丑数
- 题目
- 题目解析
思路:每个数都是2,3,5的乘积堆起来的。用a,b,c指向他们的乘积,每次更新其中最小值。
class Solution {
public:
int nthUglyNumber(int n) {
int a = 0, b = 0, c = 0;
int dp[n];
dp[0] = 1;
for(int i = 1; i < n; i++) {
int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
dp[i] = min(min(n2, n3), n5);
if(dp以上是关于剑指offer刷题动态规划的主要内容,如果未能解决你的问题,请参考以下文章