虽然物品是一个一维的线性储存
但是我们决策的过程可以看为一个树状的过程
每个物品都有选和不选两种情况
那么我们决策的过程就是在从上往下遍历这个树
利用贪心算法,根据性价比给物品排序
大的在决策树的上面
一次往下选
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
#define NUM 100
using namespace std;
int cw; // 当前重量
int cv; // 当前价值
int bestv; // 当前最优解
// 每个物品的数据结构
struct good {
int w;
int v;
double d; // 性价比
} goods[NUM];
bool cmp(good a, good b){
if(a.d >= b.d) return true;
else return false;
}
// 剪枝算法
int Bound(int i, int c, int n){
int cleft = c - cw; // 剩余容量
int max_value = cv; // 最大价值
// 尽量装满背包
// 利用循环来预先观察后面节点是否超重,而不是等后面递归
while(i < n && goods[i].w <= cleft){
cleft -= goods[i].w;
max_value += goods[i].v;
i++;
}
// 看剩余部分还能装多少价值
if(i < n) max_value += cleft * goods[i].d;
return max_value;
}
// 回溯算法
void backtrack(int i, int c, int n){
// 到叶子结点跟新最优值
if(i + 1 > n) {
bestv = cv;
return;
}
// 左子树
if(cw + goods[i].w <= c){
cw += goods[i].w;
cv += goods[i].v;
backtrack(i+1,c,n);
// 开始回溯,变成进入这个结点之前的情况
// 在为右节点做准备,因为右节点的意思不选这个物品
cw -= goods[i].w;
cv -= goods[i].v;
}
// 右节点
// Bound是在求后面最多还能装多少价值的东西
// 如果比当前最后值还好的话,继续向后面的结点计算
// 没有进入判断才是剪枝
if(Bound(i+1,c,n) > bestv) backtrack(i+1,c, n);
}
int main() {
int c = 5; // 背包的容量
int w[] = {2, 1, 3, 2};
int v[] = {12, 10, 20, 15};
int n = sizeof(w) / sizeof(w[0]);
for(int i = 0;i < n;i++){
goods[i].v = v[i];
goods[i].w = w[i];
goods[i].d = 1.0 * goods[i].v / goods[i].w;
}
sort(goods, goods + n, cmp);
backtrack(0, c, n);
cout << bestv << endl;
}
关于这个剪枝函数
如果我们不适用剪枝函数
直接往后递归的话
那每条决策的路都会被走一遍
所以递归出口的bestv
代表每走到叶子结点,我们就返回这条决策的最后答案
那这样的话我们需要得到一个最好答案的数组
最后返回数组的最大值才是最终答案
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
#define NUM 100
using namespace std;
int cw; // 当前重量
int cv; // 当前价值
int bestv; // 当前最优解
// 每个物品的数据结构
struct good {
int w;
int v;
double d; // 性价比
} goods[NUM];
bool cmp(good a, good b){
if(a.d >= b.d) return true;
else return false;
}
// 回溯算法
void backtrack(int i, int c, int n){
// 到叶子结点跟新最优值
if(i + 1 > n) {
bestv = cv;
cout << bestv << " ";
return;
}
// 左子树
if(cw + goods[i].w <= c){
cw += goods[i].w;
cv += goods[i].v;
// cout << cv << endl;
backtrack(i+1,c,n);
// 开始回溯,变成进入这个结点之前的情况
cw -= goods[i].w;
cv -= goods[i].v;
}
// 右节点
// Bound是在求后面最多还能装多少价值的东西
// 如果比当前最后值还好的话,继续向后面的结点计算
// 没有进入判断才是剪枝
// if(Bound(i+1,c,n) > bestv)
backtrack(i+1,c, n);
}
int main() {
int c = 5; // 背包的容量
int w[] = {2, 1, 3};
int v[] = {12, 10, 20};
int n = sizeof(w) / sizeof(w[0]);
for(int i = 0;i < n;i++){
goods[i].v = v[i];
goods[i].w = w[i];
goods[i].d = 1.0 * goods[i].v / goods[i].w;
}
sort(goods, goods + n, cmp);
cout << "每种决策的最终结果:" << endl;
backtrack(0, c, n);
// cout << bestv << endl;
}
这一次数据变成三个,更容易看出来
三个物品的话
总共八种情况
最后输出只有七种是因为第一种全选超重了