动态规划的定义
- 一种数学优化的方法,同时也是编程的方法
重要属性
- 最优子结构 Optimal Substructure
状态转移方程 - 重叠子问题 Overlapping Sub-problems
有的子问题结果会被多次用到
一个简单的例子/。动态规划的要点之一就是找子问题,这里可以分解为从A到B和从B到C两个子问题。 这两个子问题是比较容易求得的。但问题是题目要求不能重复,所以这个题不能这样找子结构。
例:leetcode300 最长子序列的长度
要考虑的其实就是如何根据f(1)~f(n-1)得到f(n)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> cache(nums.size(),1);//下标为i的代表以这个结尾的最长子串大小
int maxNum=0;
for(int i=0;i<nums.size();i++){
for(int j=0;j<i;j++){
//如果发现nums[j]<nums[i],说明nums[i]有机会构成上升序列
if(nums[i]>nums[j]&&cache[j]+1>cache[i]){
cache[i]=cache[j]+1;
}
}
maxNum=max(maxNum,cache[i]);
}
return maxNum;
}
};
线性规划
- 各个子问题的规模以线性的方式分布
- 子问题的最佳状态或结果可以存储在一维线性的数据结构中,例如一维数组,哈希表等。
- 通常会用dp[i]表示第i个位置的结果,或者从0开始到第i个位置为止的最佳状态或结果。
基本形式:
- 当前所求的值仅仅依赖于有限个先前计算好的值,即dp[i]仅仅依赖于有限个dp[j] (j<i)。
例1“求斐波那契数列时,dp[i]=dp[i-1]+dp[i-2] leetcode 70
例题2:leetcode 198 给定一个数组,不能选择相邻的数,求如何才能使总数最大(0 1思想)
设dp[i]为到第i个时搜到的金额总大小。
dp[i]=max(dp[i-2]+nums[i],dp[i-1])第i个房子选还是不选对应两种情况,取两种情况下的较大值
class Solution {
public:
int rob(vector<int>& nums) {
int n= nums.size();
if(n==0)return 0;
if(n==1)return nums[0];
vector <int> dp(n,-1);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<n;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[n-1];
}
};
leetcode62
设dp[i][j]为走到(i,j)坐标位置的路径个数,由于只能往右或者往下走,得到dp[i][j]=dp[i-1][j]+dp[i][j-1]其实就是填一个矩阵。最上和最左两个向量都是1。
class Solution {
public:
int uniquePaths(int m, int n) {
if(m==1||n==1)return 1;
if(m==0||n==0)return 0;
vector<vector<int>> dp(m,vector<int>(n,-1));
dp[0][0]=0;
for(int i=1;i<n;i++)dp[0][i]=1;
for(int j=1;j<m;j++)dp[j][0]=1;
for(int p=1;p<m;p++){
for(int q=1;q<n;q++){
dp[p][q]=dp[p-1][q]+dp[p][q-1];
}
}
return dp[m-1][n-1];
}
};
还有一种情况是当前所求的值依赖于所有先前计算和的值,即dp[i]是各个do[j]的某种组合,其中j由0遍历到i-1
例:在求最长上升子序列时,dp[i]=max(dp[j]+1)+1 0<=j<i
区间规划
- 各个子问题的规模由不同区间来定义
- 子问题的最佳状态或结果可以存在二维数组中
- 这类问题的时间复杂度一般为多项式时间
例:leetcode 516 最长回文子序列
dp[i][j]为i到j区间内的最长回文
递推:
首尾字符相等:dp[0][n-1]=dp[1][n-2]+2
否则:dp[0][n-1]=max(dp[1][n-1],dp[0][n-2])
也是类似填矩阵,要先填对角线。因为这个题是从中间往两边填的
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n=s.size();
vector<vector<int>> dp(n,vector<int>(n,-1));
for(int i=0;i<n;i++) dp[i][i]=1;
for(int len=2;len<=n;len++){
for(int i=0;i<n-len+1;i++){
int j=i+len-1;
if(s[i]==s[j]){
dp[i][j]=2+(len==2?0:dp[i+1][j-1]);
}else{
dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
}
}
}
return dp[0][n-1];
}
};