学习资料:
哔哩哔哩-算法-回溯法-0-1背包
CSDN-回溯法-0-1背包问题
回溯法的基本步骤:
- 所给问题,定义问题的解空间;
- 确定易于搜索的解空间树;
- 以深度优先的方式搜索解空间树,并且在搜索过程中用剪枝函数避免无效搜索。
·用约束函数考察左子树是否可行
·用限界函数考察右子树是否(有可能)最优
(找到的叶节点就是最优解)
0-1背包问题:
- 解空间树:子集树


过程详细描述:
- 当1的左子树允许进入时,将第一个物品放入背包,当2的左子树允许进入时,将第二个物品放入背包,当3的左子树允许进入时,将第三个物品放入背包,此时背包重量为45,不能允许接下来的第四个物品进入,如果进入的话,就超过背包容量了,所以将4的左子树剪枝,此时进行右子树的操作,将第五个物品放入背包,将其装满,计算4的右子树5的上界,上界=清楚的部分和不清楚的部分,即5的上界等于其父价值+背包剩余空间*其空间密度,无空间能放,不能执行左子树操作,执行右子树,找到了叶节点(i>n时为叶子结点,叶子结点是最优解),计算其上界为86,并且更新最优解的值
- 回溯找到活结点(就是还没进行右子树操作的结点),找到了结点3,进行右子树操作,计算结点3右子树4的上界,为结点3价值(物品1、物品2)、物品4价值、部分物品5价值和的结果(因为3在执行右子树操作,左子树进不去,不能取第三个物品,跳过第三个物品),结点4执行左子树5操作,取物品4,计算当前价值,无空间能放,不能执行左子树操作,执行右子树,再取的话,i>n了,为叶子结点,值为父节点的价值88,88>86,更新最优解。
- 回溯,找到活结点4,执行右子树5操作,结点3价值+物品5的价值,与结点3的重量比,还有30空间可放,执行左子树操作,但此时为叶子结点,直接将父价值赋予结点6,92>88,更新最优解
- 回溯,找到活结点5,进行右子树操作,计算其价值为42。
- 回溯,找到活结点2。
- 回溯,找到活结点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;
}
这篇博客介绍了如何运用回溯法解决0-1背包问题,通过建立解空间树,采用深度优先搜索并利用剪枝函数减少无效搜索。文章详细描述了回溯过程,展示了代码实现,包括物品的排序、上界计算和回溯策略,最终找出背包内物品的最大价值组合。
1万+

被折叠的 条评论
为什么被折叠?



