leetcode Ch2-Dynamic Programming [2014]

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

原文地址:https://www.cnblogs.com/forcheryl/p/4108637.html