算法-回溯法-0-1背包

这篇博客介绍了如何运用回溯法解决0-1背包问题,通过建立解空间树,采用深度优先搜索并利用剪枝函数减少无效搜索。文章详细描述了回溯过程,展示了代码实现,包括物品的排序、上界计算和回溯策略,最终找出背包内物品的最大价值组合。

学习资料:

哔哩哔哩-算法-回溯法-0-1背包
CSDN-回溯法-0-1背包问题

回溯法的基本步骤:

  • 所给问题,定义问题的解空间;
  • 确定易于搜索的解空间树;
  • 以深度优先的方式搜索解空间树,并且在搜索过程中用剪枝函数避免无效搜索。
    ·用约束函数考察左子树是否可行
    ·用限界函数考察右子树是否(有可能)最优
    (找到的叶节点就是最优解)

0-1背包问题:

  • 解空间树:子集树
    哈哈
    在这里插入图片描述
    过程详细描述:
  1. 当1的左子树允许进入时,将第一个物品放入背包,当2的左子树允许进入时,将第二个物品放入背包,当3的左子树允许进入时,将第三个物品放入背包,此时背包重量为45,不能允许接下来的第四个物品进入,如果进入的话,就超过背包容量了,所以将4的左子树剪枝,此时进行右子树的操作,将第五个物品放入背包,将其装满,计算4的右子树5的上界,上界=清楚的部分和不清楚的部分,即5的上界等于其父价值+背包剩余空间*其空间密度,无空间能放,不能执行左子树操作,执行右子树,找到了叶节点(i>n时为叶子结点,叶子结点是最优解),计算其上界为86,并且更新最优解的值
  2. 回溯找到活结点(就是还没进行右子树操作的结点),找到了结点3,进行右子树操作,计算结点3右子树4的上界,为结点3价值(物品1、物品2)、物品4价值、部分物品5价值和的结果(因为3在执行右子树操作,左子树进不去,不能取第三个物品,跳过第三个物品),结点4执行左子树5操作,取物品4,计算当前价值,无空间能放,不能执行左子树操作,执行右子树,再取的话,i>n了,为叶子结点,值为父节点的价值88,88>86,更新最优解。
  3. 回溯,找到活结点4,执行右子树5操作,结点3价值+物品5的价值,与结点3的重量比,还有30空间可放,执行左子树操作,但此时为叶子结点,直接将父价值赋予结点6,92>88,更新最优解
  4. 回溯,找到活结点5,进行右子树操作,计算其价值为42。
  5. 回溯,找到活结点2。
  6. 回溯,找到活结点1。

其代码如下:

#include <stdio.h>
int n = 4;//物品个数
int c = 50;//背包容量
int w[100];//每个物品的重量数组
int p[100];//每个物品的价值数组
int cw = 0;//背包当前容量
int cp = 0;//背包当前价值
int bestvalue = 0;//最优解
int ppp[100];
int x[100];//取法数组
int order[100];
void knapsack() {//按价值递增排好序
    int i, t = 0, j;
    for (i = 1;i <= n;i++) {//每个物品的单位价值
        ppp[i] = p[i] / w[i];
    }
    for (i = 1;i <= n - 1;i++) {//进行冒泡排序
        for (j = i + 1;j <= n;j++) {

            if (p[i] > p[j]) {//价值排序
                t = p[i];
                p[i] = p[j];
                p[j] = t;


                t = w[i];//重量排序
                w[i] = w[j];
                w[j] = t;

                t = order[i];//编号排序
                order[i] = order[j];
                order[j] = t;


                t = ppp[i];//对应的单位价值的排序
                ppp[i] = ppp[j];
                ppp[j] = t;

            }

        }
    }
}
int bound(int i) {//计算上界
    int t = c - cw;//剩余价值
    int b = cp;//当前价值
    while (i < n && w[i] <= t) {
        t = t - w[i];
        b = b + p[i];
        i++;
    }
    if (i < n) b = b + ppp[i] * t;
    return b;
}
void backtrack(int i) {//回溯过程
    bound(i);
    if (i > n) {//叶子结点
        bestvalue = cp;
        return;
    }
    if (cw + w[i] < c) {//左节点
        cw += w[i];
        cp += p[i];
        x[i] = 1;
        backtrack(i + 1);
        cw -= w[i];
        cp -= p[i];

    }
    if (bound(i + 1) > bestvalue) {//右结点
        backtrack(i + 1);
    }
}
main() {
    for (int i = 1;i <= n;i++) {
        printf("第%d个物品的重量为:", i);
        scanf_s("%d", &w[i]);
        printf("第%d个物品的价值为:", i);
        scanf_s("%d", &p[i]);
        order[i] = i;
    }
    knapsack();
    backtrack(1);
    printf("最优解为:%d\n", bestvalue);
    printf("取得物品编号为:");
    for (int i = 1;i <= n;i++) {
        if (x[i] == 1)
            printf("%d", order[i]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

人才一枚

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

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

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

打赏作者

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

抵扣说明:

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

余额充值