算法学习:动态规划之实战篇

12 篇文章 0 订阅

动态规划的基础篇:https://blog.csdn.net/weixin_44625138/article/details/100188659

实战分析

一、leetcode题目: 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

在此处给出两种解法:

1、解法一:将子问题的解用数组存储起来:(比较简单的动态规划法)

public  static  int climbStairs(int n){
        if(n<=2) return n;   //先判断传入的参数
        int[] temp=new int[n];  //开辟一个数组用来存储值计算过的值
        temp[0]=1;
        temp[1]=2;
        for(int i=2;i<n;i++){
            temp[i]=temp[i-1]+temp[i-2];
        }
        return temp[n-1];

   }

2、解法二:因为只是需要前两个的值,因此此处直接引用三个变量来进行记录,用来节省空间,而不用另外开辟数组:

 public  static  int climbStairs2(int n){
        if(n<=2) return n;
        int temp=0;
        int temp1=1;
        int temp2=2;
        for(int i=2;i<n;i++){
            temp=temp1+temp2;
            temp2=temp1;
            temp1=temp;

        }
        return temp;

    }

二、leetcode题目: 三角形最小路径和

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)

解法一:使用 递归+保存记忆+分治 的解法(此方法在LeetCode上会超时,当三角形的层数比较深的时候会超时)

​
private static Integer[][] memo;
    public  static int minimumTotal(List<List<Integer>> triangle) {
    	
    	
        int level=triangle.size();
        memo=new Integer[level][level];

        return getmin(0,0,triangle,level);

    }

    public static int getmin(int row,int y,List<List<Integer>> triangle,int level){
        if(memo[row][y]!=null) {
             	return memo[row][y];
         }
         if(row==level-1){
                return memo[row][y]=triangle.get(row).get(y);
         }
        	
        //此处使用到了分治
        int left=getmin(row+1,y,triangle,level);
        int right=getmin(row+1,y+1,triangle,level);
         //下一层的最小值加上本层的值
        return memo[row][y]= Math.min(left,right)+triangle.get(row).get(y);
    }

​

解法二:动态规划:

public static int GetMinDp(List<List<Integer>> triangle) {
	 int row=triangle.size();
	 
	 int[] temp=new int[row+1];
	 for(int i=triangle.size()-1;i>=0;i--) {
		 for(int j=0;j<triangle.get(i).size();j++) {
			 temp[j]=triangle.get(i).get(j)+Math.min(temp[j], temp[j+1]);
			 System.out.println(temp[j]);
		 }
		 
	 }
	 
	 return temp[0];
	 
 }

解法分析:

1、确定状态方程为temp[j];

2、确定状态转移方程为:temp[j]=triangle.get(i).get(j)+Math.min(temp[j], temp[j+1]); 

triangle.get(i).get(j)相当于(i,j)的值  temp[j]相当于i+1层的第j个数的数值,temp[j+1]相当于i+1的第j+1个数的数值, 由于只能向下或者向后移动,因此对于triangle.get(i).get(j)来说,下一层的值对应的列为j或者j+1;由于上一层的值只需要用到下一层的值,因此tem[j]的值可以被覆盖,从下往上一直递推。到了最顶层,只有一列,j此时为0,因此最后的值为 temp[0 ]

 

三、leetcode题目:编辑距离

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符

入: word1 = "horse", word2 = "ros"
输出: 3
解释: 
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

动态规划的题解如下:

class Solution {
    public int minDistance(String word1, String word2) {
        //先进行参数的判断
        if(word1==null || word2==null|| word1.equals(word2)) return 0;
        
        int m=word1.length();
        int n=word2.length();
        if(m*n==0) return m+n;   
        
        //确定状态方程,dp[i][j] 表示 word1 的前 i 个字母和 word2 的前 j 个字母之间的编辑距离
        int[][] dp=new int[m+1][n+1];
        
        //处理边界,一个空串和一个非空串的编辑距离为 dp[i][0] = i 和 dp[0][j] = j
        for(int i=0;i<m+1;i++){
            dp[i][0]=i;
        }
        
         for(int i=0;i<n+1;i++){
            dp[0][i]=i;
        }
        
        //开始进行双层循环遍历;
        
        for(int i=1;i<m+1;i++){
            for(int j=1;j<n+1;j++){
                int insert=dp[i-1][j]+1;    //如果是插入的操作
                int delete=dp[i][j-1]+1;    //如果是删除操作
                int replace=dp[i-1][j-1];   //如果是替换操作
                if (word1.charAt(i - 1) != word2.charAt(j - 1))  //不相等的话表示对于i-1处的字符需要进行操作
                   replace += 1;
                 dp[i][j] = Math.min(insert, Math.min(delete, replace));   //最后获取最小值
            }
        }
        return dp[m][n];
        
        
    }
}

 

四、Leetcode题目:零钱替换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1

解法:动态递归,自下向上

步骤:

1、确定状态方程:dp[i]  (金额为i时的硬币数量的最小值)

2、确定状态转移方程为:dp[i]=min(dp[i-coins[j]])+1  ,coins[j]表示最后一枚硬币的面值,dp[i-coins[j]]则表示除去最后一枚硬币的最小的数量;

3、初始化dp[0]=0,并把其他的值赋值为max;

复杂度分析

时间复杂度:O(Sn)O(Sn)。在每个步骤中,算法在 nn 个硬币中查找计算下一个 F(i)F(i),其中 1\leq i\leq s1≤i≤s。因此,总的来说,迭代次数是 SnSn。
空间复杂度:O(S)O(S),dp 使用的空间。

class Solution {
    public int coinChange(int[] coins, int amount) {
        if(coins==null || coins.length==0 || amount<0) return -1;
        if(amount==0) return 0;
        int max=amount+1;
        
         //dp表示金额为s时的数量最小值
       
        int[] dp=new int[max];
        
         //数组中填充数值,其实就是初始化;
        
         Arrays.fill(dp, max);
        //边界值,当金额为0时,数量值为0;
        dp[0]=0;
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.length;j++){
                //此处一定要进行判断,否则数组越界
                if(coins[j]<=i){
                dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);
                    }
            }
        }
//dp[amount] > amount 表明最后值无法被更新,也就是找不到组合的值
        
        return dp[amount] > amount ? -1 :dp[amount];

    }
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值