【Spring框架】基于JdbcTemplate对数据库增删改查

vue3 keepalive 不生效问题

  返回  

做题--背包问题(转自大佬)

2021/8/20 17:04:08 浏览:
  • 大佬详解版 这里转为对应的Java代码
    在这里插入图片描述

分类解题模板

背包问题大体的解题模板是两层循环,分别遍历物品nums和背包容量target,然后写转移方程,根据背包的分类我们确定物品和容量遍历的先后顺序,根据问题的分类我们确定状态转移方程的写法

首先是背包分类的模板:

1、0/1背包:外循环nums,内循环target,target倒序且target>=nums[i];
2、完全背包:外循环nums,内循环target,target正序且target>=nums[i];
3、组合背包(考虑顺序):外循环target,内循环nums,target正序且target>=nums[i];
4、分组背包:这个比较特殊,需要三重循环:外循环背包bags,内部两层循环根据题目的要求转化为1,2,3三种背包类型的模板

然后是问题分类的模板:

1、最值问题: dp[i] = max/min(dp[i], dp[i-nums]+1)或dp[i] = max/min(dp[i], dp[i-num]+nums);
2、存在问题(bool):dp[i]=dp[i]||dp[i-num];
3、组合问题:dp[i]+=dp[i-num];

  1. 0-1背包模板
public void ZeroOne(){
        int[] weight = {1, 3, 4};//各个物品的重量
        int[] value = {15, 20, 30};//对应的价值
        int bagWeight = 4;				//背包最大能放下多少重的物品
        //状态定义:dp[j]表示不超过j重量的物品的最大价值
        int[] dp = new int[bagWeight+1]; 
        // weight数组的大小 就是物品个数
        for(int i = 0;i<weight.length;i++){// 遍历物品
            for(int j = bagWeight;j>=weight[i];j--){// 遍历背包容量,相当于target
                //不取或者取第i个
                dp[j] = Math.max(dp[j],dp[j-weight[i]]+value[i]);
            }
        }
        System.out.println(dp[bagWeight]);
}
  1. 零钱兑换,完全背包的最值问题
public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];//dp[i]:和为i时用到硬币的最少数量
        int max = amount+1;
        Arrays.fill(dp,max);
        dp[0] = 0;
        for (int coin : coins){
            for (int i = 0; i <= amount; i++){
            	if (coin <= i)
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
        return dp[amount] >amount ? -1 : dp[amount];
}
  1. 分割等和子集:判断是否能将一个数组分割为两个子集,其和相等. 0-1背包存在性问题
public boolean canPartition(int[] nums) {
        int sum = 0;
        for(int num:nums)
            sum+=num;
        if(sum%2!=0)
            return false;
        int target = sum/2;
        boolean[] dp = new boolean[target+1];//dp[i]:是否存在子集和为i
        dp[0] = true;//初始化:target=0不需要选择任何元素,所以是可以实现的
        for(int num:nums){
            for(int j = target;j>=0;j--){
                if(j>=num)
                    dp[j] = dp[j] || dp[j-num];
            }
        }
        return dp[target];
    }
  1. 目标和,给数组里的每个数字添加正负号得到target的组合方式,0-1背包不考虑元素顺序的组合问题
/**
原问题等同于: 找到nums一个正子集P和一个负子集N,使得总和等于target。即sum(P) - sum(N) == target,
即2 * sum(N) == sum(nums)-target , 所以sum(nums)-target必须>=0且为偶数,否则等式不可能成立。
则问题转换为:存在多少个子集P,使sum(N) == (sum(nums)-target)/2。——————01背包的组合问题
 */
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for(int num:nums)
            sum+=num;
        if(sum-target<0||(sum-target)%2!=0)
            return 0;
        int newTar = (sum-target)/2;
        int[] dp = new int[newTar+1]; //dp[j]:和为j时有多少种组合方式
        dp[0] = 1;//边界条件:一个元素也不选
        for(int num :nums){
            for(int j = newTar;j>=num;j--){
                dp[j] = dp[j] + dp[j-num];//没有将num添加进来+将num添加进来
            }
        }
        return dp[newTar];
    }
  1. 完全平方数,找出若干个完全平方数使其和为n,返回完全平方数最少数量——完全背包的最值问题
//完全背包的最值问题
    public int numSquares(int n) {
        int max = n+1;
        int[] dp = new int[n+1];//dp[i]:和为i时用到的最少组合数
        Arrays.fill(dp,max);
        dp[0] = 0;dp[1] = 1;
        for(int i = 0;i<=(int)Math.sqrt(n);i++){
            for(int j = 0;j<=n;j++){
                if(j>=i*i)
                    dp[j] = Math.min(dp[j],dp[j-i*i]+1);
            }
        }
        return dp[n];
    }
  1. 组合总和IV:在nums中任选一些数,和为target.考虑顺序(组合背包)的组合问题:外循环target,内循环nums
//需要考虑顺序,因此是组合背包的组合问题
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];//dp[i]组成和为i有多少种组合方式
        dp[0] = 1; //组合问题的边界为1
        for(int i = 0;i<=target;i++){
            for(int num:nums){
                if(i>=num)
                    dp[i] = dp[i] + dp[i-num];
            }
        }
        return dp[target];
    }
  1. 零钱兑换2:任选硬币凑成指定金额,求组合总数。完全背包不考虑顺序的组合问题
//完全背包的组合问题
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount+1];//dp[i]表示凑成金额i的组合数
        dp[0] = 1;
        for(int coin:coins){
            for(int i = 0;i<=amount;i++){
                if(i>=coin)
                    dp[i] = dp[i] + dp[i-coin];//选coin或不选coin
            }
        }
        return dp[amount];
    }

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号