动态规划(Dynamic Programming, DP)
在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。因此,动态规划
保存递归时的结果,因而不会在解决同样的问题时花费时间 · · · · · · 动态规划只能应用于有最优 子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能
完全满足,故有时需要引入一定的近似)。简单地说,问题能够分解成子问题来解决。
「状态定义」
「状态转移方程」
「初始化」
「输出」
「是否可以空间优化」
5. 最长回文子串
//5. 最长回文子串
string longestPalindrome(string s) {
/* 思路说明
dp[i][j] 表示:子串 s[i..j] 是否为回文子串(子串 s[i..j]
初始状态:所有长度为 1 的子串都是回文串
从长度为len=2开始到len=n-1依次枚举所有长度的子串(注:如果len=n-1时某个字串是回文串,当len=n时只需判断此时子串的头尾位置元素是否相等即可判断)
*/
int n = s.size();
if (n < 2)
{
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
vector<vector<int>> dp(n, vector<int>(n));
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++)
{
dp[i][i] = true;
}
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= n; L++)
{
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < n; i++)
{
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= n)
{
break;
}
if (s[i] != s[j])
{
dp[i][j] = false;
}
else
{
if (j - i < 3) //长度为2,头尾元素相等 即为回文串
{
dp[i][j] = true;
}
else
{
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen)
{
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substr(begin, maxLen);
}
70. 爬楼梯
//70. 爬楼梯
int climbStairs(int n) {
/*
每次可以走一步或者两步,因此第 i 阶可以从第 i-1 或 i-2 阶到达。
即到第 i 阶的方法数即为走到第 i-1 阶的方法数加上走到第 i-2 阶的方法数。
状态转移方程:dp[i] = dp[i-1] + dp[i-2]
*/
if (n < 3)
{
return n;
}
vector<int> dp(n + 1, 1);
for (int i = 2; i <= n; ++i)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
53. 最大子数组和
//53. 最大子数组和
int maxSubArray(vector<int>& nums) {
int n = nums.size();
// dp[i] 表示:以 nums[i] 结尾的连续子数组的最大和,初始状态为dp[0]=nums[0]
vector<int> dp(n);
//初始状态即以nums[0]结尾的连续子数组的最大和即nums[0]
dp[0] = nums[0];
//从nums[1]开始遍历依次获取 i=1...n-1的所有以nums[i]结尾的连续子数组的最大和存入dp
for (int i = 1; i < n; i++) {
/*
当dp[i - 1]>0 , dp[i] = dp[i - 1] + nums[i]
当dp[i - 1]<=0, dp[i] = nums[i]
*/
if (dp[i - 1] > 0)
{
dp[i] = dp[i - 1] + nums[i];
}
else
{
dp[i] = nums[i];
}
}
// 从dp中获取连续子数组的最大和
int res = dp[0];
for (int i = 1; i < n; i++) {
res = max(res, dp[i]);
}
return res;
}
45. 跳跃游戏 II
//45. 跳跃游戏 II
int jump(vector<int>& nums) {
/*
定义steps[i]为到达第i个位置需要的步数;
当到达第i个位置时,此时可以跳跃的步数为nums[i];
所以下标范围为[i+1,i+nums[i]]的这些位置,仅仅需要一步即可到达.即steps[i+1] ..... steps[i+nums[i]] = steps[i] + 1;
*/
int len = nums.size();
// dp[i] 表示:以 到达位置i的最小步数
vector<int> dp(len, INT_MAX);
dp[0] = 0;
//当到达第i个位置时
for (int i = 0; i < len; i++)
{
//可以向后跳跃[1,nums[i]]步
for (int j = 1; j <= nums[i]; j++)
{
if (i + j < len)
{
dp[i + j] = min(dp[i + j], dp[i] + 1);
//所以steps[i+j]只需要在steps[i+j]和steps[i]+1中取最小值即可
}
}
}
return dp[len - 1];
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。