背包问题(三)--多重背包

参考资料
背包九讲
https://www.acwing.com/activity/content/11/

多重背包模型

背包容量为V,有N件物品,每件物品的体积是vi,价值是wi,第i件物品最多有mi件可用。求将物品装入背包获得的最大价值

思路
f(i,v)表示前i件商品,背包容量不大于v时的最大价值
f(i,v) = max(f(i-1,v-kc[i])+kwi) 0<=k<=mi

代码实现

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int V = sc.nextInt();
        int[] v = new int[N + 1];
        int[] w = new int[N + 1];
        int[] s = new int[N + 1];
        for (int i = 1; i <= N; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
            s[i] = sc.nextInt();
        }

        int[][] dp = new int[N + 1][V + 1];
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j <= V; j++) {
                for (int k = 0; k <= s[i] && j >= k * v[i]; k++) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k*w[i]);
                }
            }
        }
        System.out.println(dp[N][V]);
    }
}

复杂度优化
二进制优化:
通过数字1,2可以拼出1~3的任意数字
通过1,2,4可以拼出1~7的任意数字
通过1,2,4,8可以拼出1~15的任意数字
...
由此,对于第i件物品,如果有1023件,那我们可以用1,2,4,8,16,32,64,...,512拼出1023件物品
1,2,4,8,16,32...可以看成是01背包,要么选择1件商品,要么不选,...,要么选择32件商品,要么不选

本来想要凑出1~n个数需要O(n)的复杂度,现在只需要O(logn)的复杂度

总得来说,就是将每个物品分为logm份,然后做01背包。总得复杂度O(NVlogm)

import java.util.Scanner;

public class Main2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int V = sc.nextInt();
        int[] v = new int[12010]; // N * log(每种物品最多的数量)
        int[] w = new int[12010];

        int cnt = 0;
        for (int i = 1; i <= N; i++) {
            int a = sc.nextInt(); // 第i种物品的体积
            int b = sc.nextInt(); // 第i种物品的价值
            int c = sc.nextInt(); // 第i种物品的数量

            int k = 1;
            while (k <= c) {
                cnt++;
                v[cnt] = a * k;
                w[cnt] = b * k;
                c -= k;
                k *= 2;
            }
            if (c > 0) {
                cnt++;
                v[cnt] = a * c;
                w[cnt] = b * c;
            }
        }

        // 01背包
        N = cnt;
        int[][] dp = new int[N + 1][V + 1];
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j <= V; j++) {
                dp[i][j] = dp[i - 1][j];
                if(j>=v[i]){
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
                }
            }
        }
        System.out.println(dp[N][V]);
    }
}

对上面的01背包优化


        // 01背包
        N = cnt;
        int[] dp = new int[V + 1];
        for (int i = 1; i <= N; i++) {
            for (int j = V; j >= v[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
            }
        }
        System.out.println(dp[V]);

记录路径

leetcode798 得分最高的最小轮调

给定一个数组 A,我们可以将它按一个非负整数 K 进行轮调,这样可以使数组变为 A[K], 
A[K+1], A{K+2], ... A[A.length - 1], A[0], A[1], ..., A[K-1] 的形式。此后,任何值小于或等于其
索引的项都可以记作一分。

例如,如果数组为 [2, 4, 1, 3, 0],我们按 K = 2 进行轮调后,它将变成 [1, 3, 0, 2, 4]。这将记
作 3 分,因为 1 > 0 [no points], 3 > 1 [no points], 0 <= 2 [one point], 2 <= 3 [one point], 4 <= 4 
[one point]。

在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调索引 K。如果有多个答案,返
回满足条件的最小的索引 K。

示例 1:
输入:[2, 3, 1, 4, 0]
输出:3
解释:
下面列出了每个 K 的得分:
K = 0,  A = [2,3,1,4,0],    score 2
K = 1,  A = [3,1,4,0,2],    score 3
K = 2,  A = [1,4,0,2,3],    score 3
K = 3,  A = [4,0,2,3,1],    score 4
K = 4,  A = [0,2,3,1,4],    score 3
所以我们应当选择 K = 3,得分最高。
 

示例 2:
输入:[1, 3, 0, 2, 4]
输出:0
解释:
A 无论怎么变化总是有 3 分。
所以我们将选择最小的 K,即 0。

提示:
A 的长度最大为 20000。
A[i] 的取值范围是 [0, A.length]。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-rotation-with-highest-score
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

leetcode799 香槟塔

我们把玻璃杯摆成金字塔的形状,其中第一层有1个玻璃杯,第二层有2个,依次类推到第100
层,每个玻璃杯(250ml)将盛有香槟。

从顶层的第一个玻璃杯开始倾倒一些香槟,当顶层的杯子满了,任何溢出的香槟都会立刻等流
量的流向左右两侧的玻璃杯。当左右两边的杯子也满了,就会等流量的流向它们左右两边的杯
子,依次类推。(当最底层的玻璃杯满了,香槟会流到地板上)

例如,在倾倒一杯香槟后,最顶层的玻璃杯满了。倾倒了两杯香槟后,第二层的两个玻璃杯各
自盛放一半的香槟。在倒三杯香槟后,第二层的香槟满了 - 此时总共有三个满的玻璃杯。在倒
第四杯后,第三层中间的玻璃杯盛放了一半的香槟,他两边的玻璃杯各自盛放了四分之一的香
槟,如下图所示。

现在当倾倒了非负整数杯香槟后,返回第 i 行 j 个玻璃杯所盛放的香槟占玻璃杯容积的比例(i 
和 j都从0开始)。

示例 1:
输入: poured(倾倒香槟总杯数) = 1, query_glass(杯子的位置数) = 1, query_row(行数) = 1
输出: 0.0
解释: 我们在顶层(下标是(0,0))倒了一杯香槟后,没有溢出,因此所有在顶层以下的玻
璃杯都是空的。

示例 2:
输入: poured(倾倒香槟总杯数) = 2, query_glass(杯子的位置数) = 1, query_row(行数) = 1
输出: 0.5
解释: 我们在顶层(下标是(0,0)倒了两杯香槟后,有一杯量的香槟将从顶层溢出,位于
(1,0)的玻璃杯和(1,1)的玻璃杯平分了这一杯香槟,所以每个玻璃杯有一半的香槟。
注意:

poured 的范围[0, 10 ^ 9]。
query_glass 和query_row 的范围 [0, 99]。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/champagne-tower
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
image.png
全部评论

相关推荐

04-20 22:20
已编辑
门头沟学院 golang
27届,bg为四非本211硕,如题,导师不放实习,且每周至少一次线下组会(工作日),从研一上开始实习,然后我组在研一下引入了打卡机五段大厂分别是:美团到店、美团服务零售、快手电商、字节TikTok、字节CapCut。目前要结束我的第五段实习了(不会再刷第六段,好好搞学校的事,还有秋招)本来一直告诉自己的是“所有委屈到了终点再说”,过去告诉自己的终点自然还没到,但我觉得自己仿佛已经到了另一个终点,有感而发,写了这篇文章也许你会觉得为啥不尝试问问导师能不能实习,或者用其他让自己舒服的手段,我只能说,这很复杂,有导师的人自然会懂,这种一开始就把“利益冲突”摆明面上的招几乎就是不可能成功———————————————————我到底是怎么实习的?骗hr自己满勤,然后没有捷径,就是每周往返,第一段去的是北京美团,而学校在江苏,因此需要一周一次北京江苏往返,因为实习钱少,所以坐的基本是绿皮,难以入睡,下车后就是长达2小时的地铁去公司,地铁站上靠着人睡觉周末做什么?基本在做导师的科研or横向,学习的话很多时候就是尽力在晚上回到出租屋的时候学,这很难维持,但只能不断push自己如何破解打卡机?直接把打卡机偷了,或者使用指纹膜(当然我很早就做好了无法破解的准备,那就是找个长三角实习,每天早起去打卡完坐高铁去实习,从每周高铁往返变成每天)导师会压力吗?非常压力,实习的时候非常害怕微信弹出他的消息,PTSD了,有时候一周要往返两次学校,每次都跟要死了一样,之前真是情绪崩溃好几次,哈哈哈哈平时往返怎么平衡工作?我本来很晕车,为了不耽误公司和导师的进度,从车上一看电脑就头晕、吐,到后面可以随意在高铁、地铁、出租车上Coding,甚至不会再因为往返感到心累了,哈哈哈哈这一路已经淬炼出比较坚强的内心了,已经数不清多少次坐末班高铁从学校回公司,多少次凌晨6点爬起来赶车过去我会把这些当作是我人生的弯路,但现在,这些已经成为我宝贵的经验了。往后,我想我也能真正允许各种不好的情况出现了,因为我会真正把它当作我要解决的问题,而非抱怨,这又何尝不是终点呢?要照顾好身体,我不管怎么往返,一直非常在乎身体,会让自己睡够8小时,最近几星期培养早睡早起到公司健身后去工作的习惯,我觉得好身体很关键
gtgt..:很佩服,但是很恐怖,感觉已经从人类异化到高度运转的机器了
美团工作强度 2455人发布
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务