假期 2020.01.22
题目描述
仍然是 0 - 1背包问题。详细描述请见回溯算法–01背包问题
算法分析
此处采用分支界限算法,即bfs算法实现解决该问题,并且采用优先队列的存储方式。
- 存储结构,采用构造函数的目的是便于赋值,并且有两个重载函数,第一个是初始化数组,第二个是便于赋值。
struct Node {//每一个节点的信息,其中包括背包的相关信息
int cp, rp;
int rw;
int id;
int Choiced[Max_size];
Node() { memset(Choiced, 0, sizeof(Choiced)); }//解向量初始为零
Node(int _cp, int _rp, int _rw, int _id) {//构造函数,便于参数传递
cp = _cp;
rp = _rp;
rw = _rw;
id = _id;
}
};
/*背包数据记录*/
struct Goods {
int weight;
int value;
}goods[Max_size];
- 首先将一个节点入队
queue<Node> q;//创建队列
q.push(Node(0, sum_value, weight, 1));//压入一个初始节点
- 结束条件是队列为空,而找到解的结束条件是物品计算完或者背包空间不够时,更新最优解
if (current > Count || current_node.rw == 0)//最后一个物品时或者没有剩余空间时
{
if (current_node.cp >= best){
for (int i = 1; i <= Count; i++)
Best_choice[i] = current_node.Choiced[i];
best = current_node.cp;
}
continue;
}
- 当满足限制条件时,即还有空间可容纳该物品时
if (current_rw >= goods[current].weight)//小于背包容量
{
lchild.rw = current_rw - goods[current].weight;
lchild.cp = current_cp + goods[current].value;
lchild = Node{ lchild.cp,current_rp,lchild.rw,current + 1 };
for (int i = 1; i < current; i++)
lchild.Choiced[i] = current_node.Choiced[i];
lchild.Choiced[current] = 1;
if (lchild.cp > best)//最优值更新
best = lchild.cp;
q.push(lchild);//左孩子入队
}
- 后续物品不会构成最优解时
if (current_cp + current_rp >= best)//满足条件
{
rchild = Node{ current_cp,current_rp,current_rw,current + 1 };
for (int i = 1; i < current; i++)
rchild.Choiced[i] = current_node.Choiced[i];
rchild.Choiced[current] = 0;
q.push(rchild);//右孩子入队
}
- 结束该程序,直到队列为空。
代码解析
#include<iostream>
#include<algorithm>
#include <queue>
using namespace std;
constexpr auto Max_size = 20;
int Best_choice[Max_size];//最优方案存储
int best = 0,weight = 0,Count = 0,sum_weight = 0,sum_value = 0;//最优值,最大容量,物品数量,物品总重量,物品总价值
struct Node {
int cp, rp;
int rw;
int id;
int Choiced[Max_size];
Node() { memset(Choiced, 0, sizeof(Choiced)); }//解向量初始为零
Node(int _cp, int _rp, int _rw, int _id) {//构造函数,便于参数传递
cp = _cp;
rp = _rp;
rw = _rw;
id = _id;
}
};
/*背包数据记录*/
struct Goods {
int weight;
int value;
}goods[Max_size];
void bfs()
{
int current = 0, current_cp = 0, current_rp = 0, current_rw = 0;
queue<Node> q;//创建队列
q.push(Node(0, sum_value, weight, 1));//压入一个初始节点
while (!q.empty())//队列不为空时
{
Node current_node, lchild, rchild;
current_node = q.front();//取出队头元素
q.pop();//队头元素出队
current = current_node.id;//物品序号
if (current > Count || current_node.rw == 0)//最后一个物品时或者没有剩余空间时
{
if (current_node.cp >= best){
for (int i = 1; i <= Count; i++)
Best_choice[i] = current_node.Choiced[i];
best = current_node.cp;
}
continue;
}
if (current_node.cp + current_node.rp < best)//不满足最优值时
continue;
current_cp = current_node.cp;
current_rp = current_node.rp - goods[current].value;
current_rw = current_node.rw;//剩余容量
if (current_rw >= goods[current].weight)//小于背包容量
{
lchild.rw = current_rw - goods[current].weight;
lchild.cp = current_cp + goods[current].value;
lchild = Node{ lchild.cp,current_rp,lchild.rw,current + 1 };
for (int i = 1; i < current; i++)
lchild.Choiced[i] = current_node.Choiced[i];
lchild.Choiced[current] = 1;
if (lchild.cp > best)//最优值更新
best = lchild.cp;
q.push(lchild);//左孩子入队
}
if (current_cp + current_rp >= best)//满足条件
{
rchild = Node{ current_cp,current_rp,current_rw,current + 1 };
for (int i = 1; i < current; i++)
rchild.Choiced[i] = current_node.Choiced[i];
rchild.Choiced[current] = 0;
q.push(rchild);//右孩子入队
}
}
return;
}
int main()
{
cout << "请输入物品个数:";
cin >> Count;
cout << "请输入背包的容量:";
cin >> weight;
cout << "请输入各物品的重量与价值(用空格分开):" << endl;
for (int i = 1; i <= Count; i++){
cin >> goods[i].weight >> goods[i].value;
sum_weight += goods[i].weight;
sum_value += goods[i].value;
}
if (sum_weight <= weight) {
best = sum_weight;
cout << "放入的最大价值是:" << best << endl;
cout << "选择方案是将所有物品全部装入背包即可";
}
else {
bfs();
cout << "放入的最大价值是:" << best << endl;
cout << "选择方案是: ";
for (int i = 1; i <= Count; i++)
if (Best_choice[i] == 1)
cout << i << " ";
}
cout << endl;
return 0;
}
运行结果
参考
实现有所参考《趣学算法》