文章目录
刷题指南
最经典的单串
最长上升子序列(LIS)
分析:时间复杂度O( n 2 n^{2} n2)
状态表示:dp[i] : 以i为结尾的最长上升子序列长度。
状态计算:dp[i] = dp[j] + 1, (nums[j] > nums[i])
答案: dp[i]的最大值
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
for(int i = 0;i < n;i ++)
for(int j =0;j < i ;j ++ )
{
if(nums[i] > nums[j]) dp[i] = max(dp[i],dp[j] + 1);
}
int res = 0;
for(int i = 0;i < n;i ++ ) res = max(res,dp[i]);
return res;
}
};
896. 最长上升子序列 II(贪心 + 二分)
分析:时间复杂度O( n l o g n nlogn nlogn)
状态表示:q[i] :长度为i的上升子序列末尾元素的最小值
状态计算:已证明q[]严格单调递增,每次只需要二分找到nums[i]可以接到q[]数组后面,即q[x] < nums[i],
长度为x + 1,更新q[x + 1] = nums[i]
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> q(n + 1);
int len = 0;
for(int i =0 ; i< n;i ++ )
{
int l = 0, r = len;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(q[mid] < nums[i]) l = mid;
else r = mid - 1;
}
len = max(len,r + 1);
q[r + 1] = nums[i];
}
return len;
}
};
最经典的双串
最长公共子序列
分析:时间复杂度O( n 2 n^{2} n2)
状态表示:dp[i][j] : A[1~i]与B[1~j]的最长公共子序列长度
状态计算:dp[i][j] = max(dp[i-1][j], dp[i][j - 1]);
dp[i][j] = dp[i - 1][j - 1] (A[i] == B[j]);
class Solution {
public:
int longestCommonSubsequence(string s1, string s2) {
int n1 = s1.size(), n2 = s2.size();
vector<vector<int>> dp(n1 + 1,vector<int>(n2 + 1));
for(int i = 1;i <= n1;i ++ )
for(int j = 1;j <= n2;j ++ )
{
dp[i][j] = max(dp[i-1][j], dp[i][j - 1]);
if(s1[i - 1] == s2[j - 1]) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1); // 下标从1开始
}
return dp[n1][n2];
}
};
数字三角形
分析:
状态表示:dp[i][j]: 到下标为[i,j]的最短路径和
状态计算:dp[i][j] = min(dp[i-1][j],dp[i-1][j-1]) + nums[i][j],注意边界!
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
vector<vector<int>> dp(n,vector<int>(n));
dp[0][0] = triangle[0][0];
for(int i = 1;i < n ;i ++ )
for(int j = 0;j <= i; j ++ )
{
dp[i][j] = INT_MAX;
if(j < i) dp[i][j] =min(dp[i][j],dp[i - 1][j] + triangle[i][j]); // 一定要加j < i,不然溢出,因为上一层少一个元素
if(j >= 1) dp[i][j] = min(dp[i][j],dp[i-1][j-1] + triangle[i][j]);
}
int res = INT_MAX;
for(int i = 0;i < n;i ++) res = min(res,dp[n - 1][i]);
return res;
}
};
省空间的做法:&1,只需要保存上一层的状态
class Solution {
public:
int minimumTotal(vector<vector<int>>& nums) {
int n = nums.size();
vector<vector<long long>> f(2,vector<long long>(n));
f[0][0] = nums[0][0];
for(int i=1;i<n;i++)
for(int j=0;j<=i;j++)
{
f[i & 1][j] = INT_MAX;
if(j > 0) f[i & 1][j] = min(f[i & 1][j],f[i-1 & 1][j-1] + nums[i][j]);
if(j < i) f[i & 1][j] = min(f[i & 1][j],f[i-1 & 1][j] + nums[i][j]);
}
long long res = INT_MAX;
for(int i=0;i<n;i++) res = min(res,f[n-1 & 1][i]);
return res;
}
};