• 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

  • 相关阅读:
    jQuery火箭图标返回顶部代码
    类库引用EF
    Html.DropDownList
    MVC validation
    MVC @functions
    MVC 扩展方法特点
    Class 实现IDisposing方法
    MVC两个必懂核心
    Asp.net 服务器Application,Session,Cookie,ViewState和Cache区别
    sqlserver log
  • 原文地址:https://www.cnblogs.com/forcheryl/p/4108637.html
Copyright © 2020-2023  润新知