大一学了 大三复习一下巩固一下知识;

实例
java
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int b = in.nextInt();
System.out.println(a + b);
}
}
}
第一次试错(贪心):
java
import java.util.Scanner;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
Scanner scanner = new Scanner(System.in);
//箱子容量
int boxv= scanner.nextInt();
//物品数量
int object_num = scanner.nextInt();
int[] object = new int[object_num];
for(int i = 0; i < object_num; i++){
object[i] = scanner.nextInt();
}
int res = boxv;
for(int i = 0; i < object_num; i++){
if (boxv-object[i] > 0) {
boxv = Math.min(boxv - object[i], boxv);
}
}
System.out.println(boxv);
}
}
错误之处 贪心思想只能解决局部最优 无法结局全局最优
解题思路
动态规划
java
import java.util.Scanner;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
Scanner scanner = new Scanner(System.in);
//箱子容量
int boxv= scanner.nextInt();
//物品数量
int object_num = scanner.nextInt();
int[] object = new int[object_num+1];
for(int i = 0; i < object_num; i++){
object[i] = scanner.nextInt();
}
int[] dp = new int[boxv+1];//盒子中的体积
for(int i = 0; i < object_num; i++){
//每个物品只使用一次 倒序遍历
for (int j = boxv; j >= object[i]; j--){
dp [j] = Math.max(dp[j],dp[j-object[i]]+object[i]);
}
}
System.out.println(boxv-dp[boxv]);
}
}
-
状态定义 :
dp[j]
表示容量为 j 时能装入的最大总体积 。这个定义直接对应问题目标:我们需要的最终结果就是
dp[V]
(容量为 V 时的最大装入体积)。
回溯剪枝
java
import java.util.Scanner;
public class Main {
static int[][] f; // 记忆化数组
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int V = scanner.nextInt(); // 箱子容量
int n = scanner.nextInt(); // 物品数量
int[] v = new int[n]; // 物品体积数组
// 读取每个物品的体积
for (int i = 0; i < n; i++) {
v[i] = scanner.nextInt();
}
// 初始化记忆化数组,-1表示未计算
f = new int[n + 1][V + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= V; j++) {
f[i][j] = -1;
}
}
// 计算最大能装入的体积
int maxVolume = dp(v, V, n);
// 输出剩余空间
System.out.println(V - maxVolume);
}
// 递归函数:计算前n个物品,容量为V时的最大装入体积
static int dp(int[] v, int V, int n) {
// 如果已经计算过,直接返回结果
if (f[n][V] >= 0) {
return f[n][V];
}
// 边界条件:没有物品时,最大体积为0
if (n == 0) {
return 0;
}
// 当前物品体积小于等于容量,可以选或不选
if (V >= v[n - 1]) {
f[n][V] = Math.max(dp(v, V, n - 1),
dp(v, V - v[n - 1], n - 1) + v[n - 1]);
} else {
// 当前物品体积大于容量,不能选
f[n][V] = dp(v, V, n - 1);
}
return f[n][V];
}
}
-
状态转移逻辑 :
对于每个物品体积 w,有两种选择:
- 不选 :则容量为 j 时的最大体积仍为
dp[j]
。 - 选 :前提是 j≥w,此时需要用前 i−1 个物品填满 j−w 的容量,再加上当前物品的体积 w,即
dp[j-w] + w
。
因此,状态转移方程为:dp[j]=max(dp[j], dp[j−w]+w)
逆序遍历容量(从 V 到 w)是为了确保每个物品只被选一次(避免重复计算),这是 01 背包的核心优化技巧。
- 不选 :则容量为 j 时的最大体积仍为
- 回溯法(带剪枝):通过递归枚举所有可能的物品组合,并在当前体积超过容量时提前终止(剪枝)。对物品体积从大到小排序可以更快触发剪枝条件。
- 记忆化搜索:递归实现动态规划,通过记忆化数组避免重复计算,代码结构更直观。