代码随想录:动态规划11-15

46.携带研究材料

题目

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。 

小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。

第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。

第二行包含 M 个正整数,代表每种研究材料的所占空间。 

第三行包含 M 个正整数,代表每种研究材料的价值。

输出一个整数,代表小明能够携带的研究材料的最大价值。

代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();  //物品种类
        int n = sc.nextInt(); // 背包容量
        
        int[] weight = new int[m];  //每个物品的重量
        int[] value = new int[m]; //每个物品的价值
        
        for(int i=0; i < m; i++){
            weight[i] = sc.nextInt();
        }
        for(int i=0; i < m; i++){
            value[i] = sc.nextInt();
        }
        
        //dp[i][j]表示从下标0-i中任取物品,放入容量为n的背包的最大价值
        int[][] dp = new int[m][n+1]; //dp数组
        
        //dp数组初始化
        
        //第一列初始化,背包容量=0,dp全为0
        for(int i=0; i < m; i++){
            dp[i][0] = 0;
        }
        
        //第一行初始化,背包容量0-n,取物品0
        for(int j=0; j <= n; j++){
            //背包容量不够,不能取物品0
            if(j < weight[0]){
                dp[0][j] = 0;
            }
            else{
                dp[0][j] = value[0];  //可以取物品0
            }
        }
        
        //遍历顺序:从上到小+从左到右(先物品再背包)
        for(int i=1; i < m; i++){
            for(int j=1; j <= n; j++){
                //j-weight[i]小于0,下标越界,容量单独放i都不够,肯定不能取物品i
                if(j - weight[i] < 0){
                    dp[i][j] = dp[i-1][j]; 
                }
                else{
                    int no = dp[i-1][j];  //不取物品i:当前容量j的背包放不下物品i
                    int yes = dp[i-1][j-weight[i]] + value[i]; //取物品i:容量j的背包能放下物品i
                    dp[i][j] = Math.max(no, yes);
                }
            }
        }
        
       System.out.println(dp[m-1][n]);;
    }
}

416.分割等和子集

题目

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

代码

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;  //nums的元素之和
        for(int i=0; i < nums.length; i++){
            sum += nums[i];
        }
        //sum为奇数,绝对分不开
        if(sum % 2 == 1){
            return false;
        }

        //sum为偶数,其实就是在nums中找是否存在几个物品可以满足容量=sum/2的背包
        int m = nums.length; //物品种类
        int n = sum / 2; //背包容量

        int[][] dp = new int[m][n+1];  //dp[i][j]表示从0-i中任取物品,能装在背包容量j的最大价值

        //dp数组初始化
        for(int i=0; i < m; i++){
            dp[i][0] = 0;  //第一列容量为0,价值全为0
        }
        for(int j=0; j <= n; j++){
            //第一行,容量装不了物品0,价值为0
            if(j - nums[0] < 0){
                dp[0][j] = 0;
            }
            //容量能装物品0,价值为物品0价值
            else{
                dp[0][j] = nums[0];  //物品的价值就是数字大小
            }
        }

        //遍历顺序:从上到下+从左到右(先物品后背包,其实交换也行)
        for(int i=1; i < m; i++){
            for(int j=1; j <= n; j++){
                //防止j-nums[i]下标越界,容量j放不了物品i(只放物品i)
                if(j - nums[i] < 0){
                    dp[i][j] = dp[i-1][j];
                }
                else{
                    int no = dp[i-1][j];  //不取物品i:当前j的容量不能再放物品i了
                    int yes = dp[i-1][j-nums[i]] + nums[i]; //取物品i:不取且容量减去物品i的价值+物品i的价值
                    dp[i][j] = Math.max(no, yes);
                }
            }
        }
        return dp[m-1][n] == n;  //可以从集合中找到任意几个数,满足和=sum/2,返回true
    }
}

1049、最后一块石头的重量II

题目

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

输入:stones = [31,26,33,21,40]
输出:5

代码

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int m = stones.length; //物品种类
        int sum = 0; //物品总重量
        for(int i : stones){
            sum += i;
        }
        //我们希望尽可能把石头分成重量接近的两堆,才能抵消最小
        int n = sum / 2; //背包容量

        //dp[i][j]表示容量为j时,从0-1个石头里任取,达到的最大重量
        int[][] dp = new int[m][n+1];
        
        //dp数组初始化
        for(int i=0; i < m; i++){
            dp[i][0] = 0; //第一列,背包容量为0
        }
        for(int j=0; j <= n; j++){
            //第一行,能放下石头0的,dp价值就是石头0的重量
            if(j - stones[0] < 0){
                dp[0][j] = 0;
            }
            else{
                dp[0][j] = stones[0];
            }
        }

        //遍历顺序:从上到下+从左到右(先石头后重量,交换也行)
        for(int i=1; i < m; i++){
            for(int j=1; j <= n; j++){
                //背包j单独装石头i都放不下,会下标越界
                if(j - stones[i] < 0){
                    dp[i][j] = dp[i-1][j];  //按不取石头i处理
                }
                else{
                    int no = dp[i-1][j]; //不取:背包j取不了石头i了
                    int yes = dp[i-1][j-stones[i]] + stones[i]; //能取
                    dp[i][j] = Math.max(no, yes);
                }
            }
        }
        //遍历结束时,dp[m-1][n]表示容量为n时,任取石头的最大重量
        return sum - dp[m-1][n] * 2;   //最大分成dp[m-1][n]的两堆
    }
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

守岁白驹hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值