1. Triangle
1 class Solution { 2 public: 3 int minimumTotal(vector<vector<int> > &triangle) { 4 int n=triangle.size(); 5 int* res=new int[n]; 6 for(int i=0;i<n;i++) 7 res[i]=triangle[n-1][i]; 8 for(int i=n-2;i>=0;i--) 9 { 10 for(int j=0;j<=i;j++) 11 { 12 res[j]=triangle[i][j]+std::min(res[j],res[j+1]); 13 } 14 } 15 return res[0]; 16 } 17 };
2. unique paths
首先是利用了滚动数组的解法:
1 class Solution {//滚动数组 2 public: 3 int uniquePaths(int m, int n) { 4 vector<int> dp(n,0); 5 dp[0]=1; 6 for(int i=0;i<m;i++) 7 { 8 for(int j=1;j<n;j++) 9 { 10 dp[j]=dp[j-1]+dp[j]; 11 } 12 } 13 return dp[n-1]; 14 } 15 };
然后是普通二维dp数组的解法:
1 class Solution {//二维dp数组 2 public: 3 int uniquePaths(int m, int n) { 4 vector<vector<int>> dp(m,vector<int>(n,0)); 5 for(int i=0;i<m;i++) 6 dp[i][0]=1; 7 for(int i=0;i<n;i++) 8 dp[0][i]=1; 9 for(int i=1;i<m;i++) 10 { 11 for(int j=1;j<n;j++) 12 { 13 dp[i][j]=dp[i-1][j]+dp[i][j-1]; 14 } 15 } 16 return dp[m-1][n-1]; 17 } 18 };
3. unique paths II
首先是使用滚动数组的解法。注意与上一题中的不同。j循环中变为从0开始,因为要确保当obstacleGrid[i][0]为0时,更新dp[j]为0.
1 class Solution {//滚动数组 2 public: 3 int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) { 4 if(obstacleGrid.size()==0) return 0; 5 int m=obstacleGrid.size(); int n=obstacleGrid[0].size(); 6 vector<int> dp(n,0); 7 dp[0]=1; 8 for(int i=0;i<m;i++) 9 { 10 for(int j=0;j<n;j++) 11 { 12 if(obstacleGrid[i][j]==1) 13 dp[j]=0; 14 else if(j>0)//j==0时就不执行了。 15 dp[j]=dp[j]+dp[j-1]; 16 } 17 } 18 return dp[n-1]; 19 } 20 };
使用普通二维dp数组。易错点是两层for循环都是从1开始,因为涉及到dp[j-1]。
1 class Solution {//普通二维dp数组 2 public: 3 int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) { 4 if(obstacleGrid.size()==0) return 0; 5 int m=obstacleGrid.size(); int n=obstacleGrid[0].size(); 6 vector<vector<int>> dp(m,vector<int>(n,0)); 7 for(int i=0;i<m;i++) 8 { 9 if(obstacleGrid[i][0]==1) break; 10 else dp[i][0]=1; 11 } 12 for(int i=0;i<n;i++) 13 { 14 if(obstacleGrid[0][i]==1) break; 15 else dp[0][i]=1; 16 } 17 for(int i=1;i<m;i++)//注意:从1开始 18 { 19 for(int j=1;j<n;j++)//注意:从1开始 20 { 21 if(obstacleGrid[i][j]==1) dp[i][j]=0; 22 else 23 dp[i][j]=dp[i-1][j]+dp[i][j-1]; 24 } 25 } 26 return dp[m-1][n-1]; 27 } 28 };
4. minimum path sum
如果不想判断临界条件,可以分配二维dp数组时多分配1行和1列,用dp[i][j]来表示从左上角到grid[i-1][j-1]的最小路径和。
下面的解法是判断临界条件的,未采用上述技巧。
二维dp数组:
1 class Solution { 2 public: 3 int minPathSum(vector<vector<int> > &grid) { 4 if(grid.size()==0) return 0; 5 int m=grid.size();int n=grid[0].size(); 6 vector<vector<int>> dp(m,vector<int>(n,0)); 7 dp[0][0]=grid[0][0]; 8 for(int i=1;i<m;i++) 9 dp[i][0]=dp[i-1][0]+grid[i][0]; 10 for(int i=1;i<n;i++) 11 dp[0][i]=dp[0][i-1]+grid[0][i]; 12 for(int i=1;i<m;i++) 13 { 14 for(int j=1;j<n;j++) 15 { 16 dp[i][j]=grid[i][j]+std::min(dp[i-1][j],dp[i][j-1]); 17 } 18 } 19 return dp[m-1][n-1]; 20 } 21 };
滚动数组:
1 class Solution { 2 public: 3 int minPathSum(vector<vector<int> > &grid) { 4 if(grid.size()==0) return 0; 5 int m=grid.size();int n=grid[0].size(); 6 vector<int> dp(n,INT_MAX);//初始值需要设成INT_MAX,因为后面有min比较。 7 dp[0]=0; 8 for(int i=0;i<m;i++) 9 { 10 dp[0]+=grid[i][0]; 11 for(int j=1;j<n;j++) 12 { 13 dp[j]=grid[i][j]+std::min(dp[j],dp[j-1]); 14 } 15 } 16 return dp[n-1]; 17 } 18 };
5. climbing stairs
1 class Solution { 2 public: 3 int climbStairs(int n) { 4 if(n==0||n==1||n==2) return n; 5 vector<int> dp(n+1,0); 6 dp[0]=0;dp[1]=1;dp[2]=2; 7 for(int i=3;i<=n;i++) 8 { 9 dp[i]=dp[i-1]+dp[i-2]; 10 } 11 return dp[n]; 12 } 13 };
用熟了之后只用3个变量就可以,不用开辟n个元素的数组。
6. jump game
1 class Solution { 2 public: 3 bool canJump(int A[], int n) { 4 int reach=0; 5 for(int i=0;i<=reach&&i<n;i++) 6 { 7 reach=std::max(reach,A[i]+i); 8 if(reach>=n-1) return true; 9 } 10 return false; 11 } 12 };
通过变量reach来记录当前能达到的最远位置。
7. jump game II
1 class Solution { 2 public: 3 int jump(int A[], int n) { 4 int start=0,end=0,count=0; 5 int max=0; 6 if(n==1) return 0; 7 while(end<n) 8 { 9 count++; 10 for(int i=start;i<=end;i++) 11 { 12 if(A[i]+i>=n-1) return count; 13 if(A[i]+i>max)max=A[i]+i; //max表示下一轮while循环时能遍历到的最远的地方 14 } 15 start=end+1; 16 end=max; 17 } 18 19 } 20 };
start和end表示每一轮while循环能遍历的元素区域。每下一轮的start都是这一轮的end+1,保证了无缝衔接;每下一轮的end是这一轮中计算出的max,即(在下一轮while循环时)能遍历到的最远的地方。每个元素只被遍历一次,故复杂度为O(n)。最后能覆盖到下标为n-1的元素时所经历的while轮数即为最终答案(最少jump次数)。
想不通时就代入实例跑一下,立马就清晰了。
8. palindrome partitioning II
1 class Solution { 2 public: 3 int minCut(string s) { 4 const int n=s.size(); 5 vector<int> f(n+1,0); 6 vector<vector<bool>> dp(n,vector<bool>(n,0)); 7 for(int i=0;i<n+1;i++) 8 { 9 f[i]=n-1-i; 10 } 11 for(int i=n-1;i>=0;i--) 12 { 13 for(int j=i;j<n;j++) 14 { 15 if((i==j)||(s[i]==s[j]&&j==i+1)||(s[i]==s[j]&&dp[i+1][j-1]))//注意判断顺序 16 { 17 dp[i][j]=1; 18 f[i]=std::min(f[i],f[j+1]+1); 19 } 20 } 21 } 22 return f[0]; 23 } 24 };
9. word break
1 class Solution { 2 public: 3 bool wordBreak(string s, unordered_set<string> &dict) { 4 int n=s.size(); 5 if(n==0) return 0; 6 vector<bool> dp(n+1,0); 7 dp[0]=1; 8 for(int i=1;i<=n;i++)//i表示当前长度(从下标0算起) 9 { 10 for(int k=0;k<i;k++)//k表示左半子串的长度 11 {//通过k的变化,尝试每种分隔方式 12 if(dp[k]&&dict.find(s.substr(k,i-k))!=dict.end())//dp[i]表示从下标0开始的长度为i的子串是否满足word break. 13 { 14 dp[i]=1; 15 break; 16 } 17 } 18 } 19 return dp[n]; 20 } 21 };
可参考以前写的。http://www.cnblogs.com/forcheryl/p/3997304.html
10. decode ways
1 class Solution { 2 public: 3 int numDecodings(string s) { 4 int n=s.size(); 5 if(n==0) return 0; 6 vector<int> dp(n+1,0); 7 dp[0]=1; 8 if(isValid(s.substr(0,1))) 9 dp[1]=1; 10 for(int i=2;i<=n;i++) 11 { 12 if(isValid(s.substr(i-1,1))) 13 dp[i]+=dp[i-1]; 14 if(isValid(s.substr(i-2,2))) 15 dp[i]+=dp[i-2]; 16 } 17 return dp[n]; 18 } 19 private: 20 int isValid(string s) 21 { 22 if(s[0]=='0') return 0; 23 int tmp=stoi(s); 24 return tmp>=1&&tmp<=26; 25 } 26 };
关键的是先写出递推式。dp[n]=dp[n-1]*if(condition1)+dp[n-2]*if(condition2)
这里的condition1和condition2分别是判断两个对应的子串(s[i-1]、s[i-2...i-1])是否为valid。为valid时才可以累加上。
注意:dp[n]表示从下标0起的长度为n的子串的decode ways。
http://okckd.github.io/blog/2014/06/24/NineChap-Dynamic-Programming/
http://blog.csdn.net/linhuanmars/article/details/38468361