diff --git a/problems/kamacoder/0094.城市间货物运输I-SPFA.md b/problems/kamacoder/0094.城市间货物运输I-SPFA.md index 2480b12c..5fe06d34 100644 --- a/problems/kamacoder/0094.城市间货物运输I-SPFA.md +++ b/problems/kamacoder/0094.城市间货物运输I-SPFA.md @@ -158,16 +158,11 @@ 边:节点5 -> 节点6,权值为-2 ,minDist[6] > minDist[5] + (-2) ,更新 minDist[6] = minDist[5] + (-2) = 3 - 2 = 1 -如图: +如图,将节点3加入队列,因为节点6已经在队列里,所以不用重复添加 -![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240412110509.png) - - - -因为节点3 和 节点6 都曾经加入过队列,不用重复加入,避免重复计算。 - -在代码中我们可以用一个数组 visited 来记录入过队列的元素,加入过队列的元素,不再重复入队列。 +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240729161116.png) +所以我们在加入队列的过程可以有一个优化,用visited数组记录已经加入队列的元素,已经在队列的元素不用重复加入 -------------- @@ -175,11 +170,12 @@ 节点6作为终点,没有可以出发的边。 +同理从队列中取出节点3,也没有可以出发的边 + 所以直接从队列中取出,如图: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240411115424.png) - ---------- 这样我们就完成了基于队列优化的bellman_ford的算法模拟过程。 @@ -190,12 +186,12 @@ 在上面模拟过程中,我们每次都要知道 一个节点作为出发点连接了哪些节点。 -如果想方便知道这些数据,就需要使用邻接表来存储这个图,如果对于邻接表不了解的话,可以看 [kama0047.参会dijkstra堆](./kama0047.参会dijkstra堆.md) 中 图的存储 部分。 +如果想方便知道这些数据,就需要使用邻接表来存储这个图,如果对于邻接表不了解的话,可以看 [kama0047.参会dijkstra堆](./0047.参会dijkstra堆.md) 中 图的存储 部分。 整体代码如下: -```CPP +``` #include #include #include @@ -215,7 +211,9 @@ int main() { int n, m, p1, p2, val; cin >> n >> m; - vector> grid(n + 1); // 邻接表 + vector> grid(n + 1); + + vector isInQueue(n + 1); // 加入优化,已经在队里里的元素不用重复添加 // 将所有边保存起来 for(int i = 0; i < m; i++){ @@ -230,24 +228,26 @@ int main() { minDist[start] = 0; queue que; - que.push(start); // 队列里放入起点 + que.push(start); while (!que.empty()) { int node = que.front(); que.pop(); - + isInQueue[node] = false; // 从队列里取出的时候,要取消标记 for (Edge edge : grid[node]) { int from = node; int to = edge.to; int value = edge.val; if (minDist[to] > minDist[from] + value) { // 开始松弛 - minDist[to] = minDist[from] + value; - que.push(to); + minDist[to] = minDist[from] + value; + if (isInQueue[to] == false) { // 已经在队列里的元素不用重复添加 + que.push(to); + isInQueue[to] = true; + } } } } - if (minDist[end] == INT_MAX) cout << "unconnected" << endl; // 不能到达终点 else cout << minDist[end] << endl; // 到达终点最短路径 } diff --git a/problems/kamacoder/0147.三珠互斥.md b/problems/kamacoder/0147.三珠互斥.md index ed14b3ce..2ef5d392 100644 --- a/problems/kamacoder/0147.三珠互斥.md +++ b/problems/kamacoder/0147.三珠互斥.md @@ -1,11 +1,12 @@ +# 三珠互斥 + 1. 如果k * 3 大于 n 了,那说明一定没结果,如果没想明白,大家举个例子试试看 2. 分别求出三个红珠子之间的距离 3. 对这三段距离从小到大排序 y1, y2, y3 4. 如果第一段距离y1 小于k,说明需要交换 k - y 次, 同理 第二段距离y2 小于k,说明需要交换 k - y2 次 5. y1 y2 都调整好了,不用计算y3,因为 y3是距离最大 - ```CPP #include using namespace std; @@ -29,12 +30,49 @@ int main(){ sort(dis.begin(), dis.end()); int result = 0; - if (dis[0] < k) result += (k - dis[0]); if (dis[1] < k) result += (k - dis[1]); - + cout << result << endl; } return 0; } ``` + +Java代码: + +```Java +import java.util.*; + +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int t = scanner.nextInt(); + + while (t-- > 0) { + int n = scanner.nextInt(); + int k = scanner.nextInt(); + int a1 = scanner.nextInt(); + int a2 = scanner.nextInt(); + int a3 = scanner.nextInt(); + if (k * 3 > n) { + System.out.println(-1); + continue; + } + + List dis = new ArrayList<>(3); + dis.add(Math.min(Math.abs(a1 - a2), n - Math.abs(a1 - a2))); + dis.add(Math.min(Math.abs(a1 - a3), n - Math.abs(a1 - a3))); + dis.add(Math.min(Math.abs(a3 - a2), n - Math.abs(a3 - a2))); + + Collections.sort(dis); + + int result = 0; + if (dis.get(0) < k) result += (k - dis.get(0)); + if (dis.get(1) < k) result += (k - dis.get(1)); + + System.out.println(result); + } + } +} +``` diff --git a/problems/kamacoder/0148.扑克牌同花顺.md b/problems/kamacoder/0148.扑克牌同花顺.md index b5d27a24..cf8d325f 100644 --- a/problems/kamacoder/0148.扑克牌同花顺.md +++ b/problems/kamacoder/0148.扑克牌同花顺.md @@ -1,4 +1,6 @@ +# 扑克牌同花顺 + 首先我们要定义一个结构体,来存放我们的数据 `map<花色,{同一花色牌集合,同一花色的牌对应的牌数量}>` @@ -51,3 +53,70 @@ int main() { cout << sum << endl; } ``` + +Java代码如下: + +```Java + +import java.util.*; + +public class Main { + static String[] cards = {"H", "S", "D", "C"}; // 花色数组 + + static class Color { + Set st; // 同一花色牌的集合 + Map cnt; // 同一花色牌对应的数量 + + Color() { + st = new HashSet<>(); // 初始化集合 + cnt = new HashMap<>(); // 初始化映射 + } + } + + static Map umap = new HashMap<>(); // 用于存储每种花色对应的Color对象 + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); // 读取牌的数量 + + for (int i = 0; i < n; i++) { + int x = scanner.nextInt(); // 读取牌的值 + int y = scanner.nextInt(); // 读取牌的数量 + String card = scanner.next(); // 读取牌的花色 + + umap.putIfAbsent(card, new Color()); // 如果不存在该花色,则创建一个新的Color对象 + umap.get(card).st.add(x); // 将牌的值加入集合 + umap.get(card).cnt.put(x, umap.get(card).cnt.getOrDefault(x, 0L) + y); // 更新牌的数量 + } + + long sum = 0; // 结果累加器 + + // 遍历每一种花色 + for (String cardOne : cards) { + Color colorOne = umap.getOrDefault(cardOne, new Color()); // 获取对应花色的Color对象 + + // 遍历同花色的每一张牌 + for (int number : colorOne.st) { + long numberCount = colorOne.cnt.get(number); // 获取当前牌的数量 + + // 计算从当前牌到number+4的最小数量 + long cal = numberCount; + for (int j = number + 1; j <= number + 4; j++) { + cal = Math.min(cal, colorOne.cnt.getOrDefault(j, 0L)); // 更新cal为最小值 + } + + // 将结果累加到sum + sum += cal; + + // 将统计过的同花顺数量减去 + for (int j = number + 1; j <= number + 4; j++) { + colorOne.cnt.put(j, colorOne.cnt.getOrDefault(j, 0L) - cal); + } + } + } + + System.out.println(sum); // 输出结果 + } +} + +``` diff --git a/problems/kamacoder/好数组.md b/problems/kamacoder/0149.好数组.md similarity index 52% rename from problems/kamacoder/好数组.md rename to problems/kamacoder/0149.好数组.md index 55853fa8..09088168 100644 --- a/problems/kamacoder/好数组.md +++ b/problems/kamacoder/0149.好数组.md @@ -1,6 +1,7 @@ +# 149. 好数组 -算是贪心 +贪心思路: 整体思路是移动到中间位置(中位数),一定是 移动次数最小的。 @@ -11,9 +12,7 @@ 代码如下: ```CPP - #include - using namespace std; int main() { @@ -54,3 +53,50 @@ int main() { return 0; } ``` + +Java代码如下: + +```Java + +import java.util.*; + +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); + long[] arr = new long[n]; + for (int i = 0; i < n; ++i) { + arr[i] = scanner.nextLong(); + } + Arrays.sort(arr); + + if (arr[0] == arr[n - 1]) { + System.out.println(1); + return; + } + long cnt = 0L; + long cnt1 = 0L; + + // 如果要保留一个不改变,要不不改最小的,要不不改最大的。 + + // 取中间偏前的位置 + long mid = arr[(n - 2) / 2]; + + // 不改最大的 + for (int i = 0; i < n - 1; i++) { + cnt += Math.abs(arr[i] - mid); + } + + // 取中间偏后的位置 + mid = arr[n / 2]; + + // 不改最小的 + for (int i = 1; i < n; i++) { + cnt1 += Math.abs(arr[i] - mid); + } + + System.out.println(Math.min(cnt, cnt1)); + } +} + +``` diff --git a/problems/kamacoder/0150.极长连续段的权值.md b/problems/kamacoder/0150.极长连续段的权值.md index 25fbd781..503d6a23 100644 --- a/problems/kamacoder/0150.极长连续段的权值.md +++ b/problems/kamacoder/0150.极长连续段的权值.md @@ -1,21 +1,7 @@ +# 150. 极长连续段的权值 -按照动态规划的思路,每增加一位,对已有结果会有什么变化呢? - -输入 字符串s 里面有 0 和 1 - -我们先计算出 s[i-1] 的 子串权值和 a - -如果 s[i] 和 s[i - 1] 相同 - -下标0 到 i 这段字符串 的子串权值和就是 a + 1,即算上s[i]本身的连续串数量加上 - -如果 s[i] 和 s[i - 1] 不相同 - - - - - +动态规划,枚举最后边节点的情况: ```CPP #include @@ -32,9 +18,11 @@ int main() { long long a = 1; for (int i = 1; i < n; ++i) { + // 加上本身长度为1的子串 if (s[i] == s[i - 1]) { a += 1; - result += a; + result += a; + // 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串 } else { a = a + i + 1; result += a; @@ -44,3 +32,35 @@ int main() { return 0; } ``` + +Java代码如下: + +```Java +import java.util.Scanner; + +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); + String s = scanner.next(); + + long result = 1; + long a = 1; + + for (int i = 1; i < n; ++i) { + // 加上本身长度为1的子串 + if (s.charAt(i) == s.charAt(i - 1)) { + a += 1; + result += a; + // 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串 + } else { + a = a + i + 1; + result += a; + } + } + + System.out.println(result); + } +} + +``` diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index fa11fb94..0c78e3f6 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -17,21 +17,15 @@ ## 思路 +正式开始讲解背包问题! -这周我们正式开始讲解背包问题! - -背包问题的经典资料当然是:背包九讲。在公众号「代码随想录」后台回复:背包九讲,就可以获得背包九讲的pdf。 - -但说实话,背包九讲对于小白来说确实不太友好,看起来还是有点费劲的,而且都是伪代码理解起来也吃力。 - -对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。 +对于面试的话,其实掌握01背包和完全背包,就够用了,最多可以再来一个多重背包。 如果这几种背包,分不清,我这里画了一个图,如下: ![416.分割等和子集1](https://code-thinking-1253855093.file.myqcloud.com/pics/20210117171307407.png) - -至于背包九讲其他背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。 +除此以外其他类型的背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。 而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。 @@ -53,7 +47,7 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目, 这样其实是没有从底向上去思考,而是习惯性想到了背包,那么暴力的解法应该是怎么样的呢? -每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是$o(2^n)$,这里的n表示物品数量。 +每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n),这里的n表示物品数量。 **所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!** @@ -73,30 +67,111 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目, 以下讲解和图示中出现的数字都是以这个例子为例。 + ### 二维dp数组01背包 依然动规五部曲分析一波。 1. 确定dp数组以及下标的含义 -对于背包问题,有一种写法, 是使用二维数组,即**dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少**。 +我们需要使用二维数组,为什么呢? -只看这个二维数组的定义,大家一定会有点懵,看下面这个图: +因为有两个维度需要表示,分别是:物品 和 背包容量 + +如图,二维数组为 dp[i][j]。 ![动态规划-背包问题1](https://code-thinking-1253855093.file.myqcloud.com/pics/20210110103003361.png) +那么这里 i 、j、dp[i][j] 分别表示什么呢? + +i 来表示物品、j表示背包容量。 + +(如果想用j 表示物品,j表示背包容量 行不行? 都可以的,个人习惯而已) + +我们来尝试把上面的 二维表格填写一下。 + +动态规划的思路是根据子问题的求解推导出整体的最优解。 + +我们先看把物品0 放入背包的情况: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240730113455.png) + +背包容量为0,放不下物品0,此时背包里的价值为0。 + +背包容量为1,可以放下物品0,此时背包里的价值为15. + +背包容量为2,依然可以放下物品0 (注意 01背包里物品只有一个),此时背包里的价值为15。 + +以此类推。 + +再看把物品1 放入背包: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240730114228.png) + +背包容量为 0,放不下物品0 或者物品1,此时背包里的价值为0。 + +背包容量为 1,只能放下物品1,背包里的价值为15。 + +背包容量为 2,只能放下物品1,背包里的价值为15。 + +背包容量为 3,上一行同一状态,背包只能放物品0,这次也可以选择物品1了,背包可以放物品2 或者 物品1,物品2价值更大,背包里的价值为20。 + +背包容量为 4,上一行同一状态,背包只能放物品0,这次也可以选择物品1了,背包都可都放下,背包价值为35。 + +以上举例,是比较容易看懂,我主要是通过这个例子,来帮助大家明确dp数组的含义。 + +上图中,我们看 dp[1][4] 表示什么意思呢。 + +任取 物品0,物品1 放进容量为4的背包里,最大价值是 dp[1][4]。 + +通过这个举例,我们来进一步明确dp数组的含义。 + +即**dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少**。 + **要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的**,如果哪里看懵了,就来回顾一下i代表什么,j又代表什么。 2. 确定递推公式 -再回顾一下dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。 +这里在把基本信息给出来: -那么可以有两个方向推出来dp[i][j], +| | 重量 | 价值 | +| ----- | ---- | ---- | +| 物品0 | 1 | 15 | +| 物品1 | 3 | 20 | +| 物品2 | 4 | 30 | + +对于递推公式,首先我们要明确有哪些方向可以推导出 dp[i][j]。 + +这里我们dp[1][4]的状态来举例: + +绝对 dp[1][4],就是放物品1 ,还是不放物品1。 + +如果不放物品1, 那么背包的价值应该是 dp[0][4] 即 容量为4的背包,只放物品0的情况。 + +推导方向如图: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240730174246.png) + + +如果放物品1, **那么背包要先留出物品1的容量**,目前容量是4,物品1 需要重量为3,此时背包剩下容量为1。 + +容量为1,只考虑放物品0 的最大价值是 dp[0][1],这个值我们之前就计算过。 + +所以 放物品1 的情况 = dp[0][1] + 物品1 的重量,推导方向如图: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240730174436.png) + +两种情况,分别是放物品1 和 不放物品1,我们要取最大值(毕竟求的是最大价值) + +`dp[1][4] = max(dp[0][4], dp[0][1] + 物品1 的重量) ` + +以上过程,抽象化如下: + +* **不放物品i**:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。 -* **不放物品i**:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。) * **放物品i**:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值 -所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); +递归公式: `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);` 3. dp数组如何初始化 @@ -108,13 +183,13 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目, 在看其他情况。 -状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。 +状态转移方程 `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);` 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。 dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。 -那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。 +那么很明显当 `j < weight[0]`的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。 -当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。 +当`j >= weight[0]`时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。 代码初始化如下: @@ -147,7 +222,7 @@ dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化 最后初始化代码如下: -``` +```CPP // 初始化 dp vector> dp(weight.size(), vector(bagweight + 1, 0)); for (int j = weight[0]; j <= bagweight; j++) { @@ -171,7 +246,7 @@ for (int j = weight[0]; j <= bagweight; j++) { 那么我先给出先遍历物品,然后遍历背包重量的代码。 -``` +```CPP // weight数组的大小 就是物品个数 for(int i = 1; i < weight.size(); i++) { // 遍历物品 for(int j = 0; j <= bagweight; j++) { // 遍历背包容量 @@ -186,7 +261,7 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品 例如这样: -``` +```CPP // weight数组的大小 就是物品个数 for(int j = 0; j <= bagweight; j++) { // 遍历背包容量 for(int i = 1; i < weight.size(); i++) { // 遍历物品 @@ -200,7 +275,7 @@ for(int j = 0; j <= bagweight; j++) { // 遍历背包容量 **要理解递归的本质和递推的方向**。 -dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 递归公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推导出来的。 +`dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);` 递归公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推导出来的。 dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括正上方向),那么先遍历物品,再遍历背包的过程如图所示: @@ -232,50 +307,20 @@ dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括 主要就是自己没有动手推导一下dp数组的演变过程,如果推导明白了,代码写出来就算有问题,只要把dp数组打印出来,对比一下和自己推导的有什么差异,很快就可以发现问题了。 -```cpp -void test_2_wei_bag_problem1() { - vector weight = {1, 3, 4}; - vector value = {15, 20, 30}; - int bagweight = 4; - - // 二维数组 - vector> dp(weight.size(), vector(bagweight + 1, 0)); - - // 初始化 - for (int j = weight[0]; j <= bagweight; j++) { - dp[0][j] = value[0]; - } - - // weight数组的大小 就是物品个数 - for(int i = 1; i < weight.size(); i++) { // 遍历物品 - for(int j = 0; j <= bagweight; j++) { // 遍历背包容量 - if (j < weight[i]) dp[i][j] = dp[i - 1][j]; - else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); - - } - } - - cout << dp[weight.size() - 1][bagweight] << endl; -} - -int main() { - test_2_wei_bag_problem1(); -} - -``` - 本题力扣上没有原题,大家可以去[卡码网第46题](https://kamacoder.com/problempage.php?pid=1046)去练习,题意是一样的,代码如下: ```CPP - -//二维dp数组实现 #include using namespace std; -int n, bagweight;// bagweight代表行李箱空间 -void solve() { +int main() { + int n, bagweight;// bagweight代表行李箱空间 + + cin >> n >> bagweight; + vector weight(n, 0); // 存储每件物品所占空间 vector value(n, 0); // 存储每件物品价值 + for(int i = 0; i < n; ++i) { cin >> weight[i]; } @@ -294,33 +339,28 @@ void solve() { for(int i = 1; i < weight.size(); i++) { // 遍历科研物品 for(int j = 0; j <= bagweight; j++) { // 遍历行李箱容量 - // 如果装不下这个物品,那么就继承dp[i - 1][j]的值 - if (j < weight[i]) dp[i][j] = dp[i - 1][j]; - // 如果能装下,就将值更新为 不装这个物品的最大值 和 装这个物品的最大值 中的 最大值 - // 装这个物品的最大值由容量为j - weight[i]的包任意放入序号为[0, i - 1]的最大值 + 该物品的价值构成 - else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 如果装不下这个物品,那么就继承dp[i - 1][j]的值 + else { + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + } } } - cout << dp[weight.size() - 1][bagweight] << endl; -} + cout << dp[n - 1][bagweight] << endl; -int main() { - while(cin >> n >> bagweight) { - solve(); - } return 0; } + ``` ## 总结 -讲了这么多才刚刚把二维dp的01背包讲完,**这里大家其实可以发现最简单的是推导公式了,推导公式估计看一遍就记下来了,但难就难在如何初始化和遍历顺序上**。 +背包问题 是动态规划里的经典类型题目,大家要细细品味。 可能有的同学并没有注意到初始化 和 遍历顺序的重要性,我们后面做力扣上背包面试题目的时候,大家就会感受出来了。 -下一篇 还是理论基础,我们再来讲一维dp数组实现的01背包(滚动数组),分析一下和二维有什么区别,在初始化和遍历顺序上又有什么差异,敬请期待! +下一篇 还是理论基础,我们再来讲一维dp数组实现的01背包(滚动数组),分析一下和二维有什么区别,在初始化和遍历顺序上又有什么差异。 @@ -329,120 +369,42 @@ int main() { ### Java -```java -public class BagProblem { +```Java +import java.util.Scanner; + +public class Main { public static void main(String[] args) { - int[] weight = {1,3,4}; - int[] value = {15,20,30}; - int bagSize = 4; - testWeightBagProblem(weight,value,bagSize); - } + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); + int bagweight = scanner.nextInt(); - /** - * 动态规划获得结果 - * @param weight 物品的重量 - * @param value 物品的价值 - * @param bagSize 背包的容量 - */ - public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){ + int[] weight = new int[n]; + int[] value = new int[n]; - // 创建dp数组 - int goods = weight.length; // 获取物品的数量 - int[][] dp = new int[goods][bagSize + 1]; + for (int i = 0; i < n; ++i) { + weight[i] = scanner.nextInt(); + } + for (int j = 0; j < n; ++j) { + value[j] = scanner.nextInt(); + } - // 初始化dp数组 - // 创建数组后,其中默认的值就是0 - for (int j = weight[0]; j <= bagSize; j++) { + int[][] dp = new int[n][bagweight + 1]; + + for (int j = weight[0]; j <= bagweight; j++) { dp[0][j] = value[0]; } - // 填充dp数组 - for (int i = 1; i < weight.length; i++) { - for (int j = 1; j <= bagSize; j++) { + for (int i = 1; i < n; i++) { + for (int j = 0; j <= bagweight; j++) { if (j < weight[i]) { - /** - * 当前背包的容量都没有当前物品i大的时候,是不放物品i的 - * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值 - */ - dp[i][j] = dp[i-1][j]; - } else { - /** - * 当前背包的容量可以放下物品i - * 那么此时分两种情况: - * 1、不放物品i - * 2、放物品i - * 比较这两种情况下,哪种背包中物品的最大价值最大 - */ - dp[i][j] = Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i]); - } - } - } - - // 打印dp数组 - for (int i = 0; i < goods; i++) { - for (int j = 0; j <= bagSize; j++) { - System.out.print(dp[i][j] + "\t"); - } - System.out.println("\n"); - } - } -} - -``` - -```java -import java.util.Arrays; - -public class BagProblem { - public static void main(String[] args) { - int[] weight = {1,3,4}; - int[] value = {15,20,30}; - int bagSize = 4; - testWeightBagProblem(weight,value,bagSize); - } - - /** - * 初始化 dp 数组做了简化(给物品增加冗余维)。这样初始化dp数组,默认全为0即可。 - * dp[i][j] 表示从下标为[0 - i-1]的物品里任意取,放进容量为j的背包,价值总和最大是多少。 - * 其实是模仿背包重量从 0 开始,背包容量 j 为 0 的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为 0。 - * 可选物品也可以从无开始,也就是没有物品可选,即dp[0][j],这样无论背包容量为多少,背包价值总和一定为 0。 - * @param weight 物品的重量 - * @param value 物品的价值 - * @param bagSize 背包的容量 - */ - public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){ - - // 创建dp数组 - int goods = weight.length; // 获取物品的数量 - int[][] dp = new int[goods + 1][bagSize + 1]; // 给物品增加冗余维,i = 0 表示没有物品可选 - - // 初始化dp数组,默认全为0即可 - // 填充dp数组 - for (int i = 1; i <= goods; i++) { - for (int j = 1; j <= bagSize; j++) { - if (j < weight[i - 1]) { // i - 1 对应物品 i - /** - * 当前背包的容量都没有当前物品i大的时候,是不放物品i的 - * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值 - */ dp[i][j] = dp[i - 1][j]; } else { - /** - * 当前背包的容量可以放下物品i - * 那么此时分两种情况: - * 1、不放物品i - * 2、放物品i - * 比较这两种情况下,哪种背包中物品的最大价值最大 - */ - dp[i][j] = Math.max(dp[i - 1][j] , dp[i - 1][j - weight[i - 1]] + value[i - 1]); // i - 1 对应物品 i + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); } } } - // 打印dp数组 - for(int[] arr : dp){ - System.out.println(Arrays.toString(arr)); - } + System.out.println(dp[n - 1][bagweight]); } } @@ -450,137 +412,71 @@ public class BagProblem { ### Python -无参数版 ```python -def test_2_wei_bag_problem1(): - weight = [1, 3, 4] - value = [15, 20, 30] - bagweight = 4 +n, bagweight = map(int, input().split()) - # 二维数组 - dp = [[0] * (bagweight + 1) for _ in range(len(weight))] +weight = list(map(int, input().split())) +value = list(map(int, input().split())) - # 初始化 - for j in range(weight[0], bagweight + 1): - dp[0][j] = value[0] +dp = [[0] * (bagweight + 1) for _ in range(n)] - # weight数组的大小就是物品个数 - for i in range(1, len(weight)): # 遍历物品 - for j in range(bagweight + 1): # 遍历背包容量 - if j < weight[i]: - dp[i][j] = dp[i - 1][j] - else: - dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) +for j in range(weight[0], bagweight + 1): + dp[0][j] = value[0] - print(dp[len(weight) - 1][bagweight]) - -test_2_wei_bag_problem1() - -``` -有参数版 -```python -def test_2_wei_bag_problem1(weight, value, bagweight): - # 二维数组 - dp = [[0] * (bagweight + 1) for _ in range(len(weight))] - - # 初始化 - for j in range(weight[0], bagweight + 1): - dp[0][j] = value[0] - - # weight数组的大小就是物品个数 - for i in range(1, len(weight)): # 遍历物品 - for j in range(bagweight + 1): # 遍历背包容量 - if j < weight[i]: - dp[i][j] = dp[i - 1][j] - else: - dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) - - return dp[len(weight) - 1][bagweight] - -if __name__ == "__main__": - - weight = [1, 3, 4] - value = [15, 20, 30] - bagweight = 4 - - result = test_2_wei_bag_problem1(weight, value, bagweight) - print(result) +for i in range(1, n): + for j in range(bagweight + 1): + if j < weight[i]: + dp[i][j] = dp[i - 1][j] + else: + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) +print(dp[n - 1][bagweight]) ``` ### Go ```go -func test_2_wei_bag_problem1(weight, value []int, bagweight int) int { - // 定义dp数组 - dp := make([][]int, len(weight)) - for i, _ := range dp { - dp[i] = make([]int, bagweight+1) - } - // 初始化 - for j := bagweight; j >= weight[0]; j-- { - dp[0][j] = dp[0][j-weight[0]] + value[0] - } - // 递推公式 - for i := 1; i < len(weight); i++ { - //正序,也可以倒序 - for j := 0; j <= bagweight; j++ { - if j < weight[i] { - dp[i][j] = dp[i-1][j] - } else { - dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]) - } - } - } - return dp[len(weight)-1][bagweight] -} - -func max(a,b int) int { - if a > b { - return a - } - return b -} - -func main() { - weight := []int{1,3,4} - value := []int{15,20,30} - test_2_wei_bag_problem1(weight,value,4) -} ``` ### Javascript ```js -function testWeightBagProblem (weight, value, size) { - // 定义 dp 数组 - const len = weight.length, - dp = Array(len).fill().map(() => Array(size + 1).fill(0)); +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}); - // 初始化 - for(let j = weight[0]; j <= size; j++) { +let input = []; + +readline.on('line', (line) => { + input.push(line); +}); + +readline.on('close', () => { + let [n, bagweight] = input[0].split(' ').map(Number); + let weight = input[1].split(' ').map(Number); + let value = input[2].split(' ').map(Number); + + let dp = Array.from({ length: n }, () => Array(bagweight + 1).fill(0)); + + for (let j = weight[0]; j <= bagweight; j++) { dp[0][j] = value[0]; } - // weight 数组的长度len 就是物品个数 - for(let i = 1; i < len; i++) { // 遍历物品 - for(let j = 0; j <= size; j++) { // 遍历背包容量 - if(j < weight[i]) dp[i][j] = dp[i - 1][j]; - else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + for (let i = 1; i < n; i++) { + for (let j = 0; j <= bagweight; j++) { + if (j < weight[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + } } } - console.table(dp) + console.log(dp[n - 1][bagweight]); +}); - return dp[len - 1][size]; -} - -function test () { - console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6)); -} - -test(); ``` @@ -589,158 +485,63 @@ test(); ```c #include #include -#include -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define ARR_SIZE(a) (sizeof((a)) / sizeof((a)[0])) -#define BAG_WEIGHT 4 +int max(int a, int b) { + return a > b ? a : b; +} -void backPack(int* weights, int weightSize, int* costs, int costSize, int bagWeight) { - // 开辟dp数组 - int dp[weightSize][bagWeight + 1]; - memset(dp, 0, sizeof(int) * weightSize * (bagWeight + 1)); +int main() { + int n, bagweight; + scanf("%d %d", &n, &bagweight); - int i, j; - // 当背包容量大于物品0的重量时,将物品0放入到背包中 - for(j = weights[0]; j <= bagWeight; ++j) { - dp[0][j] = costs[0]; + int *weight = (int *)malloc(n * sizeof(int)); + int *value = (int *)malloc(n * sizeof(int)); + + for (int i = 0; i < n; ++i) { + scanf("%d", &weight[i]); + } + for (int j = 0; j < n; ++j) { + scanf("%d", &value[j]); } - // 先遍历物品,再遍历重量 - for(j = 1; j <= bagWeight; ++j) { - for(i = 1; i < weightSize; ++i) { - // 如果当前背包容量小于物品重量 - if(j < weights[i]) - // 背包物品的价值等于背包不放置当前物品时的价值 - dp[i][j] = dp[i-1][j]; - // 若背包当前重量可以放置物品 - else - // 背包的价值等于放置该物品或不放置该物品的最大值 - dp[i][j] = MAX(dp[i - 1][j], dp[i - 1][j - weights[i]] + costs[i]); + int **dp = (int **)malloc(n * sizeof(int *)); + for (int i = 0; i < n; ++i) { + dp[i] = (int *)malloc((bagweight + 1) * sizeof(int)); + for (int j = 0; j <= bagweight; ++j) { + dp[i][j] = 0; } } - printf("%d\n", dp[weightSize - 1][bagWeight]); -} - -int main(int argc, char* argv[]) { - int weights[] = {1, 3, 4}; - int costs[] = {15, 20, 30}; - backPack(weights, ARR_SIZE(weights), costs, ARR_SIZE(costs), BAG_WEIGHT); - return 0; -} -``` - - -### TypeScript - -```typescript -function testWeightBagProblem( - weight: number[], - value: number[], - size: number -): number { - /** - * dp[i][j]: 前i个物品,背包容量为j,能获得的最大价值 - * dp[0][*]: u=weight[0],u之前为0,u之后(含u)为value[0] - * dp[*][0]: 0 - * ... - * dp[i][j]: max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]); - */ - const goodsNum: number = weight.length; - const dp: number[][] = new Array(goodsNum) - .fill(0) - .map((_) => new Array(size + 1).fill(0)); - for (let i = weight[0]; i <= size; i++) { - dp[0][i] = value[0]; - } - for (let i = 1; i < goodsNum; i++) { - for (let j = 1; j <= size; j++) { - if (j < weight[i]) { - dp[i][j] = dp[i - 1][j]; - } else { - dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); - } - } - } - return dp[goodsNum - 1][size]; -} -// test -const weight = [1, 3, 4]; -const value = [15, 20, 30]; -const size = 4; -console.log(testWeightBagProblem(weight, value, size)); -``` - -### Scala - -```scala -object Solution { - // 01背包 - def test_2_wei_bag_problem1(): Unit = { - var weight = Array[Int](1, 3, 4) - var value = Array[Int](15, 20, 30) - var baseweight = 4 - - // 二维数组 - var dp = Array.ofDim[Int](weight.length, baseweight + 1) - - // 初始化 - for (j <- weight(0) to baseweight) { - dp(0)(j) = value(0) + for (int j = weight[0]; j <= bagweight; j++) { + dp[0][j] = value[0]; } - // 遍历 - for (i <- 1 until weight.length; j <- 1 to baseweight) { - if (j - weight(i) >= 0) dp(i)(j) = dp(i - 1)(j - weight(i)) + value(i) - dp(i)(j) = math.max(dp(i)(j), dp(i - 1)(j)) - } - - // 打印数组 - dp.foreach(x => println("[" + x.mkString(",") + "]")) - - dp(weight.length - 1)(baseweight) // 最终返回 - } - - def main(args: Array[String]): Unit = { - test_2_wei_bag_problem1() - } -} -``` - -### Rust - -```rust -pub struct Solution; - -impl Solution { - pub fn wei_bag_problem1(weight: Vec, value: Vec, bag_size: usize) -> usize { - let mut dp = vec![vec![0; bag_size + 1]; weight.len()]; - for j in weight[0]..=weight.len() { - dp[0][j] = value[0]; - } - - for i in 1..weight.len() { - for j in 0..=bag_size { - match j < weight[i] { - true => dp[i][j] = dp[i - 1][j], - false => dp[i][j] = dp[i - 1][j].max(dp[i - 1][j - weight[i]] + value[i]), - } + for (int i = 1; i < n; i++) { + for (int j = 0; j <= bagweight; j++) { + if (j < weight[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); } } - dp[weight.len() - 1][bag_size] } + + printf("%d\n", dp[n - 1][bagweight]); + + for (int i = 0; i < n; ++i) { + free(dp[i]); + } + free(dp); + free(weight); + free(value); + + return 0; } -#[test] -fn test_wei_bag_problem1() { - println!( - "{}", - Solution::wei_bag_problem1(vec![1, 3, 4], vec![15, 20, 30], 4) - ); -} ``` + +

diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index b5838c5d..c61a72cc 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -56,17 +56,31 @@ 1. 确定dp数组的定义 +关于dp数组的定义,我在 [01背包理论基础](https://programmercarl.com/背包理论基础01背包-1.html) 有详细讲解 + 在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。 2. 一维dp数组的递推公式 -dp[j]为 容量为j的背包所背的最大价值,那么如何推导dp[j]呢? +二维dp数组的递推公式为: `dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);` + +公式是怎么来的 在这里 [01背包理论基础](https://programmercarl.com/背包理论基础01背包-1.html) 有详细讲解。 + +一维dp数组,其实就上上一层 dp[i-1] 这一层 拷贝的 dp[i]来。 + +所以在 上面递推公式的基础上,去掉i这个维度就好。 + +递推公式为:`dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);` + +以下为分析: + +dp[j]为 容量为j的背包所背的最大价值。 dp[j]可以通过dp[j - weight[i]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。 -dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j]) +`dp[j - weight[i]] + value[i]` 表示 容量为 [j - 物品i重量] 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j]) -此时dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值, +此时dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取`dp[j - weight[i]] + value[i]`,即放物品i,指定是取最大的,毕竟是求最大价值, 所以递归公式为: @@ -145,10 +159,6 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15 因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。 -倒序遍历的原因是,本质上还是一个对二维数组的遍历,并且右下角的值依赖上一层左上角的值,因此需要保证左边的值仍然是上一层的,从右向左覆盖。 - -(这里如果读不懂,就再回想一下dp[j]的定义,或者就把两个for循环顺序颠倒一下试试!) - **所以一维dp数组的背包在遍历顺序上和二维其实是有很大差异的!**,这一点大家一定要注意。 5. 举例推导dp数组 @@ -158,31 +168,6 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15 ![动态规划-背包问题9](https://code-thinking-1253855093.file.myqcloud.com/pics/20210110103614769.png) - -C++代码如下: - -```CPP -void test_1_wei_bag_problem() { - vector weight = {1, 3, 4}; - vector value = {15, 20, 30}; - int bagWeight = 4; - - // 初始化 - vector dp(bagWeight + 1, 0); - for(int i = 0; i < weight.size(); i++) { // 遍历物品 - for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量 - dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); - } - } - cout << dp[bagWeight] << endl; -} - -int main() { - test_1_wei_bag_problem(); -} - -``` - 本题力扣上没有原题,大家可以去[卡码网第46题](https://kamacoder.com/problempage.php?pid=1046)去练习,题意是一样的,代码如下: ```CPP @@ -256,251 +241,229 @@ int main() { 即使代码没有通过,也会有自己的逻辑去debug,这样就思维清晰了。 -接下来就要开始用这两天的理论基础去做力扣上的背包面试题目了,录友们握紧扶手,我们要上高速啦! - - - ## 其他语言版本 ### Java ```java - public static void main(String[] args) { - int[] weight = {1, 3, 4}; - int[] value = {15, 20, 30}; - int bagWight = 4; - testWeightBagProblem(weight, value, bagWight); - } +import java.util.Scanner; - public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){ - int wLen = weight.length; - //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值 - int[] dp = new int[bagWeight + 1]; - //遍历顺序:先遍历物品,再遍历背包容量 - for (int i = 0; i < wLen; i++){ - for (int j = bagWeight; j >= weight[i]; j--){ - dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); + int bagweight = scanner.nextInt(); + + int[] weight = new int[n]; + int[] value = new int[n]; + + for (int i = 0; i < n; ++i) { + weight[i] = scanner.nextInt(); + } + for (int j = 0; j < n; ++j) { + value[j] = scanner.nextInt(); + } + + int[][] dp = new int[n][bagweight + 1]; + + for (int j = weight[0]; j <= bagweight; j++) { + dp[0][j] = value[0]; + } + + for (int i = 1; i < n; i++) { + for (int j = 0; j <= bagweight; j++) { + if (j < weight[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + } } } - //打印dp数组 - for (int j = 0; j <= bagWeight; j++){ - System.out.print(dp[j] + " "); - } + + System.out.println(dp[n - 1][bagweight]); } +} + ``` ### Python -无参版 + ```python -def test_1_wei_bag_problem(): - weight = [1, 3, 4] - value = [15, 20, 30] - bagWeight = 4 +n, bagweight = map(int, input().split()) - # 初始化 - dp = [0] * (bagWeight + 1) - for i in range(len(weight)): # 遍历物品 - for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 - dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) +weight = list(map(int, input().split())) +value = list(map(int, input().split())) - print(dp[bagWeight]) +dp = [[0] * (bagweight + 1) for _ in range(n)] +for j in range(weight[0], bagweight + 1): + dp[0][j] = value[0] -test_1_wei_bag_problem() -``` -有参版 -```python -def test_1_wei_bag_problem(weight, value, bagWeight): - # 初始化 - dp = [0] * (bagWeight + 1) - for i in range(len(weight)): # 遍历物品 - for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 - dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) +for i in range(1, n): + for j in range(bagweight + 1): + if j < weight[i]: + dp[i][j] = dp[i - 1][j] + else: + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) - return dp[bagWeight] - - -if __name__ == "__main__": - - weight = [1, 3, 4] - value = [15, 20, 30] - bagweight = 4 - - result = test_1_wei_bag_problem(weight, value, bagweight) - print(result) +print(dp[n - 1][bagweight]) ``` ### Go ```go -func test_1_wei_bag_problem(weight, value []int, bagWeight int) int { - // 定义 and 初始化 - dp := make([]int,bagWeight+1) - // 递推顺序 - for i := 0 ;i < len(weight) ; i++ { - // 这里必须倒序,区别二维,因为二维dp保存了i的状态 - for j:= bagWeight; j >= weight[i] ; j-- { - // 递推公式 - dp[j] = max(dp[j], dp[j-weight[i]]+value[i]) - } - } - //fmt.Println(dp) - return dp[bagWeight] -} - -func max(a,b int) int { - if a > b { - return a - } - return b -} +package main +import ( + "fmt" +) func main() { - weight := []int{1,3,4} - value := []int{15,20,30} - test_1_wei_bag_problem(weight,value,4) + var n, bagweight int + fmt.Scan(&n, &bagweight) + + weight := make([]int, n) + value := make([]int, n) + + for i := 0; i < n; i++ { + fmt.Scan(&weight[i]) + } + for j := 0; j < n; j++ { + fmt.Scan(&value[j]) + } + + dp := make([][]int, n) + for i := range dp { + dp[i] = make([]int, bagweight+1) + } + + for j := weight[0]; j <= bagweight; j++ { + dp[0][j] = value[0] + } + + for i := 1; i < n; i++ { + for j := 0; j <= bagweight; j++ { + if j < weight[i] { + dp[i][j] = dp[i-1][j] + } else { + dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]) + } + } + } + + fmt.Println(dp[n-1][bagweight]) } + +func max(a, b int) int { + if a > b { + return a + } + return b +} + ``` ### JavaScript ```js +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}); -function testWeightBagProblem(wight, value, size) { - const len = wight.length, - dp = Array(size + 1).fill(0); - for(let i = 1; i <= len; i++) { - for(let j = size; j >= wight[i - 1]; j--) { - dp[j] = Math.max(dp[j], value[i - 1] + dp[j - wight[i - 1]]); +let input = []; + +readline.on('line', (line) => { + input.push(line); +}); + +readline.on('close', () => { + let [n, bagweight] = input[0].split(' ').map(Number); + let weight = input[1].split(' ').map(Number); + let value = input[2].split(' ').map(Number); + + let dp = Array.from({ length: n }, () => Array(bagweight + 1).fill(0)); + + for (let j = weight[0]; j <= bagweight; j++) { + dp[0][j] = value[0]; } - } - return dp[size]; -} + + for (let i = 1; i < n; i++) { + for (let j = 0; j <= bagweight; j++) { + if (j < weight[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); + } + } + } + + console.log(dp[n - 1][bagweight]); +}); -function test () { - console.log(testWeightBagProblem([1, 3, 4, 5], [15, 20, 30, 55], 6)); -} - -test(); ``` ### C ```c #include -#include +#include -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define ARR_SIZE(arr) ((sizeof((arr))) / sizeof((arr)[0])) -#define BAG_WEIGHT 4 +int max(int a, int b) { + return a > b ? a : b; +} -void test_back_pack(int* weights, int weightSize, int* values, int valueSize, int bagWeight) { - int dp[bagWeight + 1]; - memset(dp, 0, sizeof(int) * (bagWeight + 1)); +int main() { + int n, bagweight; + scanf("%d %d", &n, &bagweight); - int i, j; - // 先遍历物品 - for(i = 0; i < weightSize; ++i) { - // 后遍历重量。从后向前遍历 - for(j = bagWeight; j >= weights[i]; --j) { - dp[j] = MAX(dp[j], dp[j - weights[i]] + values[i]); + int *weight = (int *)malloc(n * sizeof(int)); + int *value = (int *)malloc(n * sizeof(int)); + + for (int i = 0; i < n; ++i) { + scanf("%d", &weight[i]); + } + for (int j = 0; j < n; ++j) { + scanf("%d", &value[j]); + } + + int **dp = (int **)malloc(n * sizeof(int *)); + for (int i = 0; i < n; ++i) { + dp[i] = (int *)malloc((bagweight + 1) * sizeof(int)); + for (int j = 0; j <= bagweight; ++j) { + dp[i][j] = 0; } } - // 打印最优结果 - printf("%d\n", dp[bagWeight]); -} - -int main(int argc, char** argv) { - int weights[] = {1, 3, 4}; - int values[] = {15, 20, 30}; - test_back_pack(weights, ARR_SIZE(weights), values, ARR_SIZE(values), BAG_WEIGHT); - return 0; -} -``` - -### TypeScript - -```typescript -function testWeightBagProblem( - weight: number[], - value: number[], - size: number -): number { - const goodsNum: number = weight.length; - const dp: number[] = new Array(size + 1).fill(0); - for (let i = 0; i < goodsNum; i++) { - for (let j = size; j >= weight[i]; j--) { - dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); - } - } - return dp[size]; -} -const weight = [1, 3, 4]; -const value = [15, 20, 30]; -const size = 4; -console.log(testWeightBagProblem(weight, value, size)); - -``` - -### Scala - -```scala -object Solution { - // 滚动数组 - def test_1_wei_bag_problem(): Unit = { - var weight = Array[Int](1, 3, 4) - var value = Array[Int](15, 20, 30) - var baseweight = 4 - - // dp数组 - var dp = new Array[Int](baseweight + 1) - - // 遍历 - for (i <- 0 until weight.length; j <- baseweight to weight(i) by -1) { - dp(j) = math.max(dp(j), dp(j - weight(i)) + value(i)) + for (int j = weight[0]; j <= bagweight; j++) { + dp[0][j] = value[0]; } - // 打印数组 - println("[" + dp.mkString(",") + "]") - } - - def main(args: Array[String]): Unit = { - test_1_wei_bag_problem() - } -} -``` - -### Rust - -```rust -pub struct Solution; - -impl Solution { - pub fn wei_bag_problem2(weight: Vec, value: Vec, bag_size: usize) -> usize { - let mut dp = vec![0; bag_size + 1]; - for i in 0..weight.len() { - for j in (weight[i]..=bag_size).rev() { - if j >= weight[i] { - dp[j] = dp[j].max(dp[j - weight[i]] + value[i]); - } + for (int i = 1; i < n; i++) { + for (int j = 0; j <= bagweight; j++) { + if (j < weight[i]) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); } } - dp[dp.len() - 1] } + + printf("%d\n", dp[n - 1][bagweight]); + + for (int i = 0; i < n; ++i) { + free(dp[i]); + } + free(dp); + free(weight); + free(value); + + return 0; } -#[test] -fn test_wei_bag_problem2() { - println!( - "{}", - Solution::wei_bag_problem2(vec![1, 3, 4], vec![15, 20, 30], 4) - ); -} ```