算法设计与分析(期末复习版5.2)
求解0/1背包问题
问题描述:设n个物体编号为1~n,重量用数组w[1…n]表示,价值用数组v[1…n]表示,限制重量用W表示,求解在限制重量约束下,选择那些物体能使物体价值最大
问题分析:选择物体要求体重不超过约束体重W,同时需要满足最大价值,那么我们要设置一个初始变量maxv=0用于后续选择物体之后进行比较交换。这个问题是选择物体,类似于求集合的子集问题,可以将问题的解过程想象成一棵树,将解空间想象成一棵树,定义一个数组x[ i ]来表明是否选择第i个物体,从第一个物体开始作为树的根节点,是否选择,分为两棵子树的根节点,第二层在第一层选择物体与否的基础上是否选择第二个物体,以此类推,直到最后一个物体,即 i(代表索引)指向n为止i。
1.递归框架设计方法
下面我们给出第一中递归框架设计的求解程序代码
#include <stdio.h> // 包含标准输入输出库
#define MAXN 20 // 定义最大物品数量的宏
int n = 4, W = 6; // 定义物品数量和背包容量
int w[] = {0, 5, 3, 2, 1}; // 定义每个物品的重量数组
int v[] = {0, 4, 4, 3, 1}; // 定义每个物品的价值数组
int x[MAXN]; // 定义一个数组来记录每个物品是否被选中
int maxv = 0; // 定义一个变量来记录最大价值,应该定义为全局变量
// 深度优先搜索函数,用于找到最大价值的组合
void dfs(int i, int tw, int tv, int op[]) {
if (i > n) { // 如果已经检查完所有物品
if (tw == W && tv > maxv) { // 如果当前总重量等于背包容量且当前价值大于已知的最大价值
maxv = tv; // 更新最大价值
for (int j = 1; j <= n; j++) { // 复制当前最优解的选项
x[j] = op[j]; //op[]数组复制x[]数组,便于后续输出结果
}
}
} else {
op[i] = 1; // 选择第i个物品
dfs(i + 1, tw + w[i], tv + v[i], op); // 递归调用dfs,考虑选择当前物品的情况
op[i] = 0; // 不选择第i个物品
dfs(i + 1, tw, tv, op); // 递归调用dfs,考虑不选择当前物品的情况
}
}
// 打印解决方案的函数
void dispasolution() {
printf("选择的方案是: ");
for (int i = 1; i <= n; i++) { // 从1开始循环,因为数组索引从1开始
if (x[i] == 1) { // 如果第i个物品被选中
printf("%d ", i); // 打印物品的索引
}
}
printf("\n");
}
// 主函数
int main() {
int op[MAXN] = {0}; // 初始化决策数组,所有物品初始都不被选择
dfs(1, 0, 0, op); // 从第一个物品开始dfs搜索
printf("最大价值为: %d\n", maxv); // 打印出找到的最大价值
dispasolution(); // 打印出选择的物品方案
return 0;
}
对左子树进行优化,选择第i个物体放入背包(不超重,超重时左孩子剪枝),相当于在选择之前,进行了一个计算,计算tw+w[i]是否会超过约束条件W=6,如果超过的话不选择第i个物体,直接跳过,进行下一层判断。优化后深度优先遍历核心代码如下:
2.回溯法求解过程
void dfs(int i,int tw,int tv,int op[])
{
if(i>n)
{
if(tw==W && tv>maxv)
{
maxv=tv;
for(int j=1;j<=n;j++)
x[j]=op[j];
}
}
else{
if(tw+w[i]<=W)
{
op[i]=1;
dfs(i+1,tw+w[i],tv+v[i],op);
}
else
{
op[i]=0;
dfs(i+1,0,0,op);
}
}
}