leetcode 动态规划2(java实现)

#64.最小路径和

这是一个经典的动态规划题,类似于走楼梯问题。

1.定义状态:

本题目是让求从左上角到右下角最短路径,且只能向下或向右。那么我们可以缩小范围,假设与起点相邻的下面节点和右面节点是更小范围的矩形的左上角。那么求这个大矩形的状态(最短路径)就是选择向右和向下中路径相对较短的。所以最小状态就是每个节点到右下角节点的最短距离。

2.确定临界状态:

由题意,我们知道在最下面行和最右列的节点只能有一种选择。所以有式子:

dp[dp.size-1][IndexOfCol]+=dp[dp.size-1][IndexOfCol+1],
dp[IndexOfRow][dp[0].size-1]+=dp[IndexOfRow+1][dp[0].size-1] 0<=IndexOfRow<dp.size 0<=IndexOfCol<dp[0].size

不过如果路建立数组记录每个状态,空间复杂度就会达到O(n^2),显然开销是巨大的。我们可以直接在原数组上修改

grid[IndexOfRow][grid[0].length-1]+=grid[IndexOfRow+1][grid[0].length-1];
grid[grid.length-1][IndexOfCol]+=gird[grid.length-1][IndexOfCol+1]; 0<=IndexOfRow<dp.size 0<=IndexOfCol<dp[0].size

3.状态转移方程

grid[i][j]=min(grid[i+1][j],grid[i][j+1])(i<=0<grid.length-1,j<=0<grid[0].length-1)

4.代码实现:

    class Solution {
    public int minPathSum(int[][] grid) {
    for(int i=grid.length-1;i>=0;i--)
    {
   		 for(int j=grid.length-1;j>=0;j--)
    	 { 
    		if(i==grid.length-1&&j!=grid[0].length-1)
    		{
   				 grid[i][j]+=grid[i][j + 1];
    		}
    		else if(j==grid[0].length-1&&i!=grid.length-1)
    		{
    			grid[i][j]+=grid[i + 1][j];
   			}
       		else if(j!=grid[0].length-1&&i!=grid.length-1)
       		{
   				 grid[i][j]+= Math.min(grid[i + 1][j],grid[i][j + 1]);
       		}
   		 }
    }
    return grid[0][0];
    }
    }
    

时间复杂度:O(mn)

空间复杂度:O(1)

#70.爬楼梯

经典的动态规划题

1.定义状态

我们可以把问题分解成最小的问题,即计算1-n(n为楼层数)中到每层楼梯的方法有多少种。由于上楼是层层递进的,而由题目可知上楼无非是从该层往下数的第二层爬两个台阶,或者从该层往下数的第一层爬一个台阶(前提是该楼层的层数大于2)。这样通过递推就可以求出从起点到n层爬楼梯的方法数了。

2.临界状态

有两个特例,即第一层是无法用前两层的方法数相加。从起点到第一层只有一种方法。

3.状态转义方程

dp[i]=dp[i-1]+dp[i-2](2<=i<=n)

4.代码实现

  class Solution {
  	public int climbStairs(int n) {
  	int []dp=new int[n+1];//定义数组记录到每层的方法数dp[n]就是起点到第n层的方法数
  	if(n<2)
  	{
  		return 1;
	  }
  	dp[1]=1;
 	    dp[2]=2;
      for(int i=3;i<=n;i++)
      {
  	   dp[i]=dp[i-1]+dp[i-2]; 
      }
  	return dp[n];
  	}
  }

这样的空间复杂度未免有点高,可以定义两个变量代替dp[i-1],dp[i-2]

  class Solution {
  	public int climbStairs(int n) {
       if(n<2)
 		 {
  	   return 1;
  	 }
       int oldpre=1;
       int pre=1;
       int ans=0;
       for(int i=2;i<=n;i++)
       {
           ans=oldpre+pre;
           oldpre=pre; 
           pre=ans;
       }
       return ans;
     }
  }

时间复杂度:O(n)

空间复杂度:O(1)

看状态转移方程其实可以发现这道题就是求斐波那契数列,斐波那契数列的具体定义(体现了动态规划中的递推思想):

如果设F(n)为该数列的第n项(n∈N*),那么这句话可以写成如下形式:

F(n)=F(n-1)+F(n-2)

有关斐波那契数列的问题在这里就不做过多的阐述了。

91.解码方法


这道题在leetcode上的通过率只有20%,看来还是有挑战性的。

1.定义状态

老套路,把大问题分解为可解的小问题。题目要求算出解码的方法数,可以递推求每个字符结尾的子字符串的解码方法,就得到了整个字符串的解码方法有多少种。

2.临界状态

这个题比较繁琐的一点要考虑各种状态。由于A-Z编码是对应1-26的,因此还要考虑0的情况。如果是0,还要判断前一位以及这个0的下标,为0就说明没法解码,直接返回0。下标1或者更大的情况还要看前一位的数字。是1或2,如果下标为1且前面的数字是1或2,说明字符串可以编码为J或T。以该数字为结束的子字符串的编码方法就为1,如果下标更大只需在满足前面数字的为1或2时,令记录该处的值改为下标为i-2处的值。其他情况都为直接返回0(字符串无法编码)。

如果不是0的话也有很多种情况。如果假设s[i]为当前下标处的数字,如果s[i]>0且s[i]<7且s[i-1]=1或者s[i-1]=2,说明编码的方式有很多种,dp[i]=dp[i-1]+dp[i-2]。否则dp[i]=dp[i-1]

3.定义状态方程

dp[i]=dp[i-1]+dp[i-2] (s[i]==1||s[i]==2&&s[i]>0&&s[i]<7)

4.代码实现

    class Solution {
    public int numDecodings(String s) {
    	int []dp=new int[n];
   	    for(int i=0;i<s.length();i++)
        {
            if(s[i]=='0')
            { 
                if(i==0)
                {
                   return 0;
                }
                else if(i==1)
                {
                    if(s[i-1]=='1'||s[i-1]=='2')
                    {
                         dp[i]=1;
                    }
                    else
                    {
                         return 0;
                    }
                }
                else
                {
                    if(s[i-1]=='1'||s[i-1]=='2')
                    {
                        dp[i]=dp[i-2];
                    }
                    else
                    {
                        return 0;
                    }
                }
           }
          else
          {
              if(i==0)
              {
                   dp[i]=1;
              }
             else if(i==1)
             {
                 if(s[i-1]=='1'||(s[i-1]=='2'&&s[i]>'0'&&s[i]<'7'))
                 {
                    dp[i]=2;
                 }
                else
                {
                   dp[i]=dp[i-1];
                }
             }
            else
            {
                if(s[i-1]=='1'||(s[i-1]=='2'&&s[i]>'0'&&s[i]<'7'))
                 {
                        dp[i]=dp[i-1]+dp[i-2];
                 }
                 else
                 {
                     dp[i]=dp[i-1];
                 }
            }
         }
      }
    return dp[s.length()-1]}
    }

时间复杂度:O(N)

空间复杂度:O(N)

发布了9 篇原创文章 · 获赞 0 · 访问量 192

猜你喜欢

转载自blog.csdn.net/zydbk123456/article/details/104778890