蛮力法解决0/1背包问题

什么是背包问题?

问题描述:给定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$

蛮力法:

算法设计中的蛮力法(也称为穷举法、枚举法或暴力法)是一种简单直接的问题解决方法。这种方法通常基于问题的描述和所涉及的概念定义,对问题所有可能的状态(或结果)进行一一测试,直到找到解或将全部可能的状态都测试一遍为止。

蛮力法的核心思想是采用遍历(扫描)技术,即采用一定的策略将待求解问题的所有元素依次处理一次,从而找出问题的解。在这个过程中,避免重复处理已经处理过的元素是关键。

蛮力法适用于以下几种情况:

  1. 搜索所有的解空间:当问题的解存在于规模不大的解空间中时,可以使用蛮力法搜索所有可能的解。
  2. 搜索所有的路径:在需要找出特定解的问题中,不同的路径对应不同的解,使用蛮力法可以搜索所有可能的路径并计算路径对应的解。
  3. 直接计算:基于问题的描述直接进行计算时,可以使用蛮力法。
  4. 模拟和仿真:根据问题要求直接模拟或仿真时,蛮力法也是一个可行的选择。

蛮力法的优点包括逻辑简单清晰、算法复杂度高但合理、解决问题的领域广、适用于一些规模较小且时间复杂度要求不高的问题,以及可以作为其他高效算法的衡量标准。然而,蛮力法的主要缺点是缺乏人的思考,算法往往设计得简单而缺乏高效率。因此,尽管蛮力法在某些情况下是一种有效的解决方法,但在需要高效算法的情况下,通常需要考虑其他更优化的算法。

                                                              (如图解)

#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;
}

代码解析:

  1. 宏定义和全局变量

    • _CRT_SECURE_NO_WARNINGS:用于在Visual Studio中禁用安全警告。
    • MAX_ITEMS 和 MAX_VALUE:分别定义了物品的最大数量和可能的最大价值。
    • nweightvaluemax_subset 和 count:全局变量,用于存储问题的一些参数和结果。
  2. 结构体定义

    • Item:表示一个物品,具有重量(weight)、价值(value)和一个标记(flag),用于表示该物品是否已被选中。
  3. 函数定义

    • recursion:递归函数,用于尝试所有可能的物品组合。
      • 它首先检查当前的总重量是否超过了背包的容量,如果是,则返回。
      • 然后,它检查当前的总价值是否超过了之前记录的最大价值。如果是,则更新最大价值和对应的子集。
      • 接下来,它遍历从当前物品开始的剩余物品,并将每个物品标记为已选中,然后递归调用自身。
      • 在递归调用之后,它将物品标记为未选中,以便进行回溯。
  4. 主函数

    • 首先,它提示用户输入货物的数量和背包的容量,并读取这些值。
    • 然后,它提示用户输入每个物品的重量和价值,并读取这些值。
    • 接着,它调用recursion函数来找出最大价值的子集。
    • 最后,它打印出最大价值的子集和该子集的总价值。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

窦鑫锐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值