什么是背包问题?
问题描述:给定n个重量为{w1, w2, … ,wn}、价值为{v1, v2, … ,vn}的物品和一个容量为C的背包,求这些物品中的一个最有价值的子集,且要能够装到背包中。
示例:
背包容量C=15kg
物品1:重量2kg,价值2$
物品2:重量12kg,价值4$
物品3:重量1kg,价值2$
物品4:重量1kg,价值1$
物品5:重量4kg,价值10$
蛮力法:
算法设计中的蛮力法(也称为穷举法、枚举法或暴力法)是一种简单直接的问题解决方法。这种方法通常基于问题的描述和所涉及的概念定义,对问题所有可能的状态(或结果)进行一一测试,直到找到解或将全部可能的状态都测试一遍为止。
蛮力法的核心思想是采用遍历(扫描)技术,即采用一定的策略将待求解问题的所有元素依次处理一次,从而找出问题的解。在这个过程中,避免重复处理已经处理过的元素是关键。
蛮力法适用于以下几种情况:
- 搜索所有的解空间:当问题的解存在于规模不大的解空间中时,可以使用蛮力法搜索所有可能的解。
- 搜索所有的路径:在需要找出特定解的问题中,不同的路径对应不同的解,使用蛮力法可以搜索所有可能的路径并计算路径对应的解。
- 直接计算:基于问题的描述直接进行计算时,可以使用蛮力法。
- 模拟和仿真:根据问题要求直接模拟或仿真时,蛮力法也是一个可行的选择。
蛮力法的优点包括逻辑简单清晰、算法复杂度高但合理、解决问题的领域广、适用于一些规模较小且时间复杂度要求不高的问题,以及可以作为其他高效算法的衡量标准。然而,蛮力法的主要缺点是缺乏人的思考,算法往往设计得简单而缺乏高效率。因此,尽管蛮力法在某些情况下是一种有效的解决方法,但在需要高效算法的情况下,通常需要考虑其他更优化的算法。
(如图解)
#define _CRT_SECURE_NO_WARNINGS
#include <stdbool.h>
#include <stdio.h>
#define MAX_ITEMS 100 // 假设最大物品数量为100
#define MAX_VALUE 9999 // 假设最大价值为一个较大的数
int n; // 货物的总个数
int weight; // 背包承受上限
int value = 0; // 最大价值
int max_subset[MAX_ITEMS]; // 存储最大价值对应的子集
int count = 0; // 子集数量
typedef struct {
int weight; // 重量
int value; // 价值
bool flag; // 标记是否被挑选
} Item;
Item pack[MAX_ITEMS];
void recursion(int x, int current_weight, int current_value) {
// 判断重量是否超过背包承受上限
if (current_weight > weight) {
return;
}
// 判断最大价值
if (current_value > value) {
value = current_value;
count = 0;
// 将最大价值所对应的子集放入max_subset中
for (int i = 0; i < n; i++) {
if (pack[i].flag) {
max_subset[count++] = i;
}
}
}
for (int i = x; i < n; i++) {
pack[i].flag = true;
recursion(i + 1, current_weight + pack[i].weight, current_value + pack[i].value);
// 回溯
pack[i].flag = false;
}
}
int main() {
printf("请输入货物的总数量以及背包能承受的最大重量:\n");
scanf("%d %d", &n, &weight);
printf("请依次输入每件物品的重量和价值:\n");
for (int i = 0; i < n; i++) {
scanf("%d %d", &pack[i].weight, &pack[i].value);
}
recursion(0, 0, 0);
printf("最大总价值的子集为: ");
for (int i = 0; i < count; i++) {
printf("%d ", max_subset[i] + 1); // 索引从0开始,但通常我们期望从1开始计数
}
printf("\n最大总价值最大可以为: %d\n", value);
return 0;
}
代码解析:
-
宏定义和全局变量:
_CRT_SECURE_NO_WARNINGS
:用于在Visual Studio中禁用安全警告。MAX_ITEMS
和MAX_VALUE
:分别定义了物品的最大数量和可能的最大价值。n
、weight
、value
、max_subset
和count
:全局变量,用于存储问题的一些参数和结果。
-
结构体定义:
Item
:表示一个物品,具有重量(weight
)、价值(value
)和一个标记(flag
),用于表示该物品是否已被选中。
-
函数定义:
recursion
:递归函数,用于尝试所有可能的物品组合。- 它首先检查当前的总重量是否超过了背包的容量,如果是,则返回。
- 然后,它检查当前的总价值是否超过了之前记录的最大价值。如果是,则更新最大价值和对应的子集。
- 接下来,它遍历从当前物品开始的剩余物品,并将每个物品标记为已选中,然后递归调用自身。
- 在递归调用之后,它将物品标记为未选中,以便进行回溯。
-
主函数:
- 首先,它提示用户输入货物的数量和背包的容量,并读取这些值。
- 然后,它提示用户输入每个物品的重量和价值,并读取这些值。
- 接着,它调用
recursion
函数来找出最大价值的子集。 - 最后,它打印出最大价值的子集和该子集的总价值。