动态规划各类问题分析——LeetCode习题精讲
目錄
1.動態(tài)規(guī)劃問題分類
2.線性DP
???? ?? 一維數(shù)組
???? ?? 二維坐標系
???? ?? LIS(最長上升子序列)
???? ?? LCS(最長公共子序列)
???? ?? 01背包
???? ?? 完全背包
???? ?? 多重背包
???? ??
???? ??
???? ??
???? ??
1.動態(tài)規(guī)劃問題分類
2.線性DP
2.1一維數(shù)組
思路:
一個連續(xù)子數(shù)組一定要以一個數(shù)作為結尾,那么我們可以將狀態(tài)定義成如下:
dp[i]:表示以 nums[i] 結尾的連續(xù)子數(shù)組的最大和。
那么想得到dp[i],nums[i]我們是一定會選取的,根據(jù)dp[i-1]的大小我們分為兩種情況
dp[i-1]大于0的話:dp[i]=dp[i-1]+nums[i]
dp[i-1]小于0的話:dp[i]=nums[i](加上dp[i-1]反而還沒有一定會選取的nums[i]大)
C++代碼:
class Solution { public:int maxSubArray(vector<int>& nums) {if(nums.empty())return 0;vector<int> dp(nums.size(),0);dp[0]=nums[0];int max=nums[0];for(int i=1;i<nums.size();i++){if(dp[i-1]>=0){dp[i]=dp[i-1]+nums[i];if(dp[i]>max)max=dp[i];}else{dp[i]=nums[i];if(dp[i]>max)max=dp[i];}}return max;} };
思路:
我們定義dp[i]為偷盜至第i個房子時,所能獲取的最大利益。那我們可以想到,由于不可以在相鄰的房屋闖入,所以 至i房屋可盜竊的最大值,要么就是至 i-1 房屋可盜竊的最大值,要么就是至 i-2 房屋可盜竊的最大值加上當前房屋的值,二者之間取最大值。即:
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
這里要注意對于第二間房間偷盜的最大金額為前兩間房間所有的金額的最大值
C++代碼:
class Solution{ public:int rob(vector<int>& nums) {if(nums.empty())return 0;if(nums.size()>=2)nums[1]=max(nums[0],nums[1]);for(int i=2;i<nums.size();i++)nums[i]=max(nums[i]+nums[i-2],nums[i-1]);return nums[nums.size()-1];} };2.2二維數(shù)組
思路:
我們定義d[i][j]:
dp[i][j] : 表示包含第i行j列元素的最小路徑和
無論最后的路徑是哪一條,它一定要經(jīng)過最頂上的元素,即[0,0]。所以我們需要對dp[0][0]進行初始化。
dp[0][0] = [0][0]位置所在的元素值
每一行最左邊的元素只能從自己頭頂而來: dp[i][j]=dp[i-1][j]+triangle[i][j];
每一行最右邊的元素只能從自己左上角而來: dp[i][j]=dp[i-1][j-1]+triangle[i][j];
其余的元素是從左邊或者頭頂元素兩種途徑而言(既然是要求最小,那么取兩種途徑的最小值): dp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+triangle[i][j];
C++代碼:
class Solution { public:int minimumTotal(vector<vector<int>>& triangle) {int size=triangle.size();int dp[size][size];memset(dp,0,sizeof(dp));dp[0][0]=triangle[0][0];for(int i=1;i<size;i++){for(int j=0;j<=i;j++){if(j==0)dp[i][j]=dp[i-1][j]+triangle[i][j];else if(j==i)dp[i][j]=dp[i-1][j-1]+triangle[i][j];elsedp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+triangle[i][j];}}int min=INT_MAX;for(int i=0;i<size;i++){if(min>dp[size-1][i])min=dp[size-1][i];}return min;} };思路:
本題可以不使用任何額外的空間就能解答
我們先分析第一行,我們在第一行從左往右掃描,如果是0那么就有1種走法就把這個位置的元素變成1,如果有一個位置有障礙物,那么包含這個位置在內(nèi),障礙物后邊元素的走法都是0。這可以用標志位在程序中實現(xiàn),定義一個flag=0,如果找到一個障礙物后標志位置1。
我們沒掃描元素之前,元素的0和1是代表有無障礙物,掃描之后代表的是這個位置有多少種走法
我們再分析第一列,同樣的第一列只要有1個位置是障礙物,包含障礙物在內(nèi)走法都是0
對于其他的,是障礙物,那么障礙物所在位置的走法一定是0,對于不是障礙物,那么走法是左邊元素和上邊元素的和(即使左邊和上邊的元素是障礙物也無所謂,因為障礙物的走法是0)
C++代碼實現(xiàn):
class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m=obstacleGrid.size();int n=obstacleGrid[0].size();int flag=0;int f=0;if(obstacleGrid[0][0])return 0;for(int i=0;i<m;i++){for(int j=0;j<n;j++){if(i==0&&j!=0&&obstacleGrid[i][j]==0&&flag==0)obstacleGrid[i][j]=1;else if(i==0&&j!=0&&obstacleGrid[i][j]==1){obstacleGrid[i][j]=0;flag=1;}else if(j==0&&obstacleGrid[i][j]==0&&f==0)obstacleGrid[i][j]=1;else if(j==0&&obstacleGrid[i][j]==1){obstacleGrid[i][j]=0;f=1;}else{if(obstacleGrid[i][j]==0&&i>0&&j>0)obstacleGrid[i][j]=obstacleGrid[i-1][j]+obstacleGrid[i][j-1];elseobstacleGrid[i][j]=0; }}}return obstacleGrid[m-1][n-1];} };2.3LIS
本題我們可以按照w從小到大排序,w相同的按照h從大到小(這樣整個問題就變成了求排序后h的最大增長子序列),再用動態(tài)規(guī)劃求解
C++代碼:
bool com(vector<int> &a,vector<int> &b){if(a[0]>b[0])return false;else if(a[0]<b[0])return true;else{if(a[1]<b[1])return false;else return true;} } class Solution { public:int maxEnvelopes(vector<vector<int>>& envelopes) {if(!envelopes.size()) return 0;sort(envelopes.begin(),envelopes.end(),com);vector<int> res(envelopes.size(),1);vector<int> vec;int max1=0;for(int i=0;i<envelopes.size();i++)vec.push_back(envelopes[i][1]);for(int i=0;i<vec.size();i++){for(int j=0;j<i;j++){if(vec[i]>vec[j])res[i]= max(res[i],res[j]+1);}if(max1<res[i])max1=res[i];}return max1; }};2.4LCS
子序列類型的問題,窮舉出所有可能的結果都不容易,而動態(tài)規(guī)劃算法做的就是窮舉 + 剪枝,它倆天生一對兒。所以可以說只要涉及子序列問題,十有八九都需要動態(tài)規(guī)劃來解決。
本題我們可以使用二維數(shù)組來做dp[i][j]的意思就是text1的前i個字符與text2的前j個字符的最大公共序列
因此可以得出:
現(xiàn)在對比的這兩個字符不相同的,那么我們要取它的「要么是text1往前退一格,要么是text2往前退一格,兩個的最大值」
dp[i + 1][j + 1] = Math.max(dp[i+1][j], dp[i][j+1]);
對比的兩個字符相同,去找它們前面各退一格的值加1即可:dp[i+1][j+1] = dp[i][j] + 1;
class Solution { public:int longestCommonSubsequence(string text1, string text2) {int m=text1.size(),n=text2.size(); int dp[m+1][n+1];memset(dp,0,sizeof(dp));for (int i=1;i<=m;i++) {for (int j=1;j<=n;j++) {if (text1[i-1]==text2[j-1]) {dp[i][j]=dp[i-1][j-1]+1;} else {dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}return dp[m][n];} };2.3 01背包
總結
以上是生活随笔為你收集整理的动态规划各类问题分析——LeetCode习题精讲的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美团面试动态规划——整数拆分
- 下一篇: 关于printf()与自增自减运算符结和