leetcode刷题思路-----背包

leetcode刷题思路-----背包

背包问题一直是比较头疼的一类算法,但按照模板来走,比较简单。
以零钱兑换为例,其本质还是dp的状态转换。

1.基本思路

		int[][] dp = new int[coins.length+1][amount+1];
		//初始化状态
		for(int i=; i<=; i++){
            dp[0][] = ;
            dp[][0] = ;
        }
        //先遍历硬币,再遍历里面的总数
        for(int i=; i<=coins.length; i++){
            for(int j=; j<=amount; j++){
            	//按照提议选择从哪种状态转换而来
            	dp[i][j] = (dp[i-1][j],dp[i][j-coins[i-1]);       
            }
        }
        return dp[coins.length][amount];
  1. 对于数组要注意大小,一般加一比较稳妥:
//数组第一维i代表选择了前i个硬币的状态,j代表当前凑的钱的面值
int[][] dp = new int[coins.length+1][amount+1];
  1. 对于带0的初始化要规定好,比如有些初始状态不为0:
for(int i=; i<=; i++){
	dp[0][] = ?;
    dp[][0] = ?;
}
  1. 状态转移一般两种:1.来自上次的结果 2.加入当前硬币
//选择上一次的状态
dp[i][j] = dp[i-1][j];
//当加入当前硬币coins[i-1]的状态,相当于没用这个硬币而去凑amount-这个硬币的状态
dp[i][j] = ...dp[i-1][j-coins[i-1]];
//如果硬币是无限的,则第二个为i,大家可以仔细思考
dp[i][j] = ...dp[i][j-coins[i-1]];

具体例子:

1.leetcode518 零钱兑换2

/*
    给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
    请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
    假设每一种面额的硬币有无限个。
* */
private static int change2(int[] coins,int amount){

        int[][] dp = new int[coins.length+1][amount+1];
        //当金额为0时,那只有一种组合就是什么都不做,coins选空集,组合数为1;
        for (int i = 0; i <= coins.length; i++) {
            dp[i][0] = 1;
        }
        //开始遍历
        for (int i = 1; i <=coins.length; i++) {
            for(int j=1; j<=amount; j++){
            	//对于数组越界的状态要有判断
                if(j<coins[i-1]){
                	//只能按照前i-1个硬币去凑
                    dp[i][j] = dp[i-1][j];
                }else{
                	//这次不仅仅可以用i-1个硬币去凑,这次加入当前硬币去凑
                	//相当于凑j-当前硬币的组合数和之前之和
                	//例如1,2去凑10,有若干种方法,再来一个5,其实多了哪些组合呢?
                	//相当于1,2,5去凑10-5的面额的组合数(硬币是无限的)
                    dp[i][j] = dp[i-1][j]+dp[i][j-coins[i-1]];
                }
            }
        }
        return dp[coins.length][amount];
    }

2.优化

对于二维dp,面试时会要求状态压缩,而原理就是在于滚动更新:

//本质是直接在旧的dp更新新值
dp[i] = dp[i]+...;
private  static  int better_change2(int[] coins,int amount){
        int[] dp = new int[amount+1];
        dp[0] = 1;
        //注意到i都由上一次转换而来,直接去掉i循环的数组,滚动起来
        for (int i = 1; i <=coins.length; i++) {
        	//对于不更新的i直接跳过
        	//注意!!!这次从前向后遍历,因为第二个dp[i][j-coins[i-1]]先更新
            for(int j=coins[i-1]; j<=amount; j++){            
                dp[j] += dp[j-coins[i-1]];
            }
        }
        return dp[amount];
    }

其他题大家可以按照此套路继续探索。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值