一、目的
1、题目
0-1背包问题:有5个商品,重量分别为8 16 21 17 12,价值分别为8 14 16 11 7,背包的载重量为37,求装入背包的商品及其价值。
2、算法设计与代码实现
(1)设计数据结构;
(2)针对示例图1,画图并描述搜索、剪枝与回溯过程,画图并描述利用分支限界法求解0-1背包问题的过程;
(3)设计算法伪码或画流程图;
(4)编码实现。
3、测试:设计测试数据集,编写测试程序,用于测试
(1)时间复杂性的渐进分析,要求推演过程;
(2)时间复杂性的实验分析,要求编制数据表和散点图;
(3)简述空间复杂性。
二、实验内容与设计思想
1.设计思路
- 输入数据:背包的容量,物体的数量,物体的重量和价值。
- 数据预处理:将物体根据性价比进行排序
- 分支定界法进行求解:将背包状态放入优先队列中,每次取出一个,并放入该结点的拓展节点。直到优先队列为空。
- 打印结果:输出背包的价格与重量,背包内所有的物品。
2. 主要数据结构
结构名 | 数据结构 | 结构释义 |
Bag | | 释义:背包 属性: 物品数量n 物品goods 上界top与下界bottom 最大可承载重量MAX_WEIGHT |
Good | | 释义:物品 属性: 重量weight,价格value,性价比profit |
node | | 释义:优先队列的节点 属性: 此时背包的重量weight 此时背包的物品价格value 此时背包可达上界profit 背包内物品最大下标index 该节点的父节点parent |
3. 主要代码结构
Good结构体
{
构造函数
}
Bag结构体
{
构造函数
CreatData函数
PretreatmentData函数
}
node结构体
{
构造函数
}
input函数
print函数
BABM函数
main主函数
三、实验使用环境
软件:Visual Studio 2022/1/4
平台:win10
四、实验步骤和调试过程
1. 输入数据
代码:
Bag input()
{
int n, mode;
cout << "请输入物品的数量:";
cin >> n;
cout << "请选择模式:[0]手动输入 [1]自动生成" << endl;
cin >> mode;
Bag bag(n);
if (!mode)
{
int* weight = new int[n], * value = new int[n];
cout << "请输入物品的价格";
for (int i = 0; i < n; i++)
scanf("%d", &value[i]);
cout << "请输入物品的重量";
for (int i = 0; i < n; i++)
scanf("%d", &weight[i]);
cout << "请输入背包的承载重量";
scanf("%d", &bag.MAX_WEIGHT);
for (int i = 0; i < n; i++)
bag.goods.push_back(Good(weight[i], value[i]));
}
else
bag.CreatData();
return bag;
}
伪代码:
Function input()
{
n, mode : integer;
Print : "请输入物品的数量:";
Read : n;
Print : "请选择模式:[0]手动输入 [1]自动生成" << endl;
Read : mode;
Bag bag(n);
if (!mode)
{
int* weight = new int[n], * value = new int[n];
Print : "请输入物品的价格";
for i <- 0 To n
Read : "%d", &value[i];
Print : "请输入物品的重量";
for i <- 0 To n
Read : "%d", &weight[i];
Print : "请输入背包的承载重量";
Read : "%d", &bag.MAX_WEIGHT;
for i <- 0 To n
bag.goods.push_back(Good(weight[i], value[i]));
\}
Else
bag.CreatData();
return bag;
\}
2. 数据预处理
代码:
typedef struct Bag
{
int n;
vector<Good>goods;
double bottom, top;
int MAX_WEIGHT;
void CreatData()
{
srand(time(0));
for (int i = 0; i < n; i++)
{
int w = rand() % 30 + 1, v = rand() % 50 + 1;
goods.push_back(Good(w, v));
}
printf("重量:");
for (int i = 0; i < n; i++)
printf("%d ", goods[i].weight);
printf("\n");
printf("价格:");
for (int i = 0; i < n; i++)
printf("%d ", goods[i].value);
printf("\n");
MAX_WEIGHT = 100;
}
void PretreatmentData()
{
sort(goods.begin(), goods.end(), cmp_Good);
top = MAX_WEIGHT * goods[0].profit;
bottom = 0;
double w = 0;
for (int i = 0; i < goods.size(); i++)
{
if (w + goods[i].weight > MAX_WEIGHT)
continue;
bottom += goods[i].value;
w += goods[i].weight;
}
}
Bag(int n)
{
this->n = n;
}
}Bag;
伪代码:
typedef struct Bag
{
n : integer;
vector<Good>goods;
bottom, top : double;
MAX_WEIGHT : integer;
void CreatData()
{
srand(time(0));
for i <- 0 To n
{
w <- rand() % 30 + 1, v <- rand() % 50 + 1 : integer;
goods.push_back(Good(w, v));
}
Print : "重量:";
for i <- 0 To n
Print : "%d ", goods[i].weight;
Print : "\n";
Print : "价格:";
for i <- 0 To n
Print : "%d ", goods[i].value;
Print : "\n";
MAX_WEIGHT <- 100;
}
void PretreatmentData()
{
sort(goods.begin(), goods.end(), cmp_Good);
top <- MAX_WEIGHT * goods[0].profit;
bottom <- 0;
w <- 0 : double;
for i <- 0 To goods.size()
{
if (w + goods[i].weight > MAX_WEIGHT)
continue;
bottom += goods[i].value;
w += goods[i].weight;
}
}
Bag(n)
{
this->n <- n;
}
}Bag;
预处理结果:
序号 | 重量 | 价格 | 性价比 |
1 | 8 | 8 | 1 |
2 | 16 | 14 | 0.875 |
3 | 21 | 16 | 0.761 |
4 | 17 | 11 | 0.647 |
5 | 12 | 7 | 0.583 |
3. 分支定界法
代码:
typedef struct node
{
int weight;
int value;
double profit;
int index;
node* parent;
bool operator<(const node& a) const
{
return this->profit > a.profit; //大顶堆
}
node(int w, int v, double p, int index, node* parent)
{
weight = w;
value = v;
profit = p;
this->index = index;
this->parent = parent;
}
}node;
//Branch and bound method分支定界法
node* BABM(Bag bag)
{
node* ans = new node(0, 0, 0, -1, NULL);
//剪枝:当背包内所有物品的重量少于MAX_WEIGHT
int w = 0;
for (int i = 0; i < bag.n; i++)
{
w += bag.goods[i].weight;
}
if (w <= bag.MAX_WEIGHT)
{
for (int i = 0; i < bag.n; i++)
{
int weight = ans->weight + bag.goods[i].weight;
int value = ans->value + bag.goods[i].value;
node* next = new node(weight, value, 0, i, ans);
ans = next;
}
return ans;
}
priority_queue<node*, vector<node*>>q;
q.push(ans);
while (q.size())
{
node* front = q.top();
q.pop();
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (front->profit < ans->value)
continue;
if (front->value > ans->value)
ans = front;
int index = front->index + 1;
if (index >= bag.n)
continue;
int w1 = front->weight + bag.goods[index].weight;
int v1 = front->value + bag.goods[index].value;
double p1 = v1 + (bag.MAX_WEIGHT - w1) * bag.goods[index+1].profit;
node* lchild = new node(w1, v1, p1, index, front);
double p2 = front->value + (bag.MAX_WEIGHT - front->weight) * bag.goods[index+1].profit;
node* rchild = new node(front->weight, front->value, p2, index, front);
//剪枝:当超重,舍弃
if (lchild->weight <= bag.MAX_WEIGHT)
q.push(lchild);
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (lchild->profit >= ans->value || lchild->profit < bag.bottom)
q.push(rchild);
}
return ans;
}
伪代码:
function BABM(Bag bag)
{
node* ans <- new node(0, 0, 0, -1, NULL);
//剪枝:当背包内所有物品的重量少于MAX_WEIGHT
w <- 0 : integer;
for i <- 0 To bag.n
w += bag.goods[i].weight;
if (w <= bag.MAX_WEIGHT)
{
for i <- 0 To bag.n
{
weight <- ans->weight + bag.goods[i].weight;
value <- ans->value + bag.goods[i].value;
node* next <- new node(weight, value, 0, i, ans);
ans <- next;
}
return ans;
}
priority_queue<node*, vector<node*>>q;
q.push(ans);
while (q.size())
{
node* front <- q.top();
q.pop();
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (front->profit < ans->value)
continue;
if (front->value > ans->value)
ans <- front;
index <- front->index + 1 : integer;
if (index >= bag.n)
continue;
w1 <- front->weight + bag.goods[index].weight : integer;
v1 <- front->value + bag.goods[index].value : integer;
p1 <- v1 + (bag.MAX_WEIGHT - w1) * bag.goods[index].profit : double;
node* lchild <- new node(w1, v1, p1, index, front);
p2 <- front->value + (bag.MAX_WEIGHT - front->weight) * bag.goods[index].profit : double;
node* rchild <- new node(front->weight, front->value, p2, index, front);
//剪枝:当超重,舍弃
if (lchild->weight <= bag.MAX_WEIGHT)
q.push(lchild);
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (lchild->profit >= ans->value || lchild->profit < bag.bottom)
q.push(rchild);
/}
return ans;
/}
描述:
①:初始化优先队列q(使用stl中priority_queue实现,优先队列的排序实现)
②:给当前最优解ans赋0/空,并放入q中
③:while循环内部不断取出节点,若优于最优解,则替换最优解。随后扩展结点,直到q中无节点为止。
④:首先判断当前扩展结点的左儿子,若左儿子为可行结点(没有超重),则加入优先队列中。然后判断当前扩展结点的右儿子结点,求得其上界,若上界少于当前最优解价格或者少于下届,则丢弃。,否则加入优先队列。
⑤:从优先队列中取下一结点,继续循环。循环结束后,返回最优解。
剪枝:
第一次剪枝:
判断当前所有物品总重:若总重少于承载重量,则直接将最优解设为ALL_IN。
第二次剪枝:
从优先队列取出节点时,判断其价格上界是否少于当前最优解价格:若否,则舍弃。
第三次剪枝:
拓展左节点时:如果超重,则舍弃,不放入队列。
第四次剪枝:
拓展右节点是:如果上界少于当前最优解价格,则舍弃,不放入队列。
实验:
序号:-1 重量:0 价格:0 上界:37.000
序号:0 重量:0 价格:0 上界:37.000
序号:0 重量:8 价格:8 上界:37.000
序号:1 重量:8 价格:8 上界:33.375
序号:2 重量:29 价格:24 上界:30.095
左节点剪枝
序号:3 重量:29 价格:24 上界:29.176
左节点剪枝
序号:2 重量:8 价格:8 上界:30.095
序号:4 重量:29 价格:24 上界:28.667
序号:1 重量:24 价格:22 上界:33.375
左节点剪枝
序号:2 重量:24 价格:22 上界:31.905
左节点剪枝
序号:3 重量:24 价格:22 上界:30.412
序号:4 重量:24 价格:22 上界:29.583
序号:3 重量:8 价格:8 上界:26.765
序号:4 重量:20 价格:15 上界:24.917
序号:3 重量:25 价格:19 上界:26.765
序号:4 重量:25 价格:19 上界:26.000
序号:1 重量:0 价格:0 上界:32.375
序号:2 重量:0 价格:0 上界:28.190
右节点剪枝
序号:3 重量:17 价格:11 上界:23.941
序号:2 重量:21 价格:16 上界:28.190
左节点剪枝
序号:3 重量:21 价格:16 上界:26.353
序号:4 重量:21 价格:16 上界:25.333
序号:4 重量:33 价格:23 上界:25.333
序号:4 重量:36 价格:29 上界:29.583
序号:4 重量:8 价格:8 上界:24.917
序号:1 重量:16 价格:14 上界:32.375
序号:2 重量:16 价格:14 上界:30.000
右节点剪枝
序号:3 重量:33 价格:25 上界:27.588
序号:2 重量:37 价格:30 上界:30.000
左节点剪枝
序号:3 重量:37 价格:30 上界:30.000
左节点剪枝
序号:4 重量:37 价格:30 上界:30.000
序号:4 重量:37 价格:26 上界:26.000
最大价值:30
最大重量:37
包中的物品有:2 1 0
4. 打印结果
代码:
void print(node* ans)
{
printf("最大价值:%d\n", ans->value);
printf("最大重量:%d\n", ans->weight);
printf("包中的物品有:");
while (ans->index != -1)
{
printf("%d ", ans->index);
ans = ans->parent;
}
}
伪代码:
function print(node* ans)
{
Print : "最大价值:%d\n", ans->value;
Print : "最大重量:%d\n", ans->weight;
Print : "包中的物品有:";
while (ans->index != -1)
{
Print : "%d ", ans->index;
ans <- ans->parent;
\}
\}
复杂度分析
时间复杂度渐近分析
输入数据:O(N)
输入N个物品的数据(重量,价值)
数据预处理:O(N)
根据性价比对物品进行排序:O(N^2)
根据物品1的性价比计算上界:O(1)
根据所有物品计算下界:O(N)
分支定界法:
最坏情况:无剪枝情况
最好情况:O(N):即所有的物品中只有一个物品能被装下。。
打印结果:O(N)
时间复杂度实验分析(数据表+散点图)
数据表
物品数量 | 进入优先队列数量 | 时间(ms) | 数据(重量&价值) |
3 | 7 | 0 | (1,48)(10,32)(46,44) |
5 | 433 | 0 | (1,48)(4,42)(10,32)(46,44)(41,28) |
7 | 1843 | 0 | (1,48)(4,42)(10,32)(17,34)(46,44)(41,28)(20,13) |
9 | 7837 | 0 | (1,48)(4,42)(10,32)(17,34)(46,44)(48,34)(41,28)(20,13)(27,16) |
11 | 18452 | 0 | (1,48)(4,42)(10,32)(17,34)(46,44)(48,34)(41,28)(20,13)(27,16)(41,20)(34,14) |
12 | 19032 | 1 | (1,48)(4,42)(10,32)(17,34)(46,44)(48,34)(41,28)(20,13)(27,16)(41,20)(34,14)(40,13) |
13 | 36425 | 0 | (1,36)(4,27)(9,41)(16,44)(19,28)(49,45)(49,42)(48,41)(15,12)(37,27)(25,9)(4,1)(9,2) |
14 | 37469 | 1 | (1,36)(4,27)(9,41)(16,44)(19,28)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(25,9)(4,1)(9,2) |
14 | 38453 | 1 | (1,36)(4,27)(9,41)(16,44)(19,28)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(25,9)(4,1)(9,2) |
15 | 64365 | 2 | (1,36)(4,27)(9,41)(16,44)(19,28)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
15 | 65434 | 1 | (1,36)(4,27)(9,41)(16,44)(19,28)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
16 | 66606 | 2 | (1,36)(4,27)(9,41)(16,44)(19,28)(29,35)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
16 | 67289 | 1 | (1,36)(4,27)(9,41)(16,44)(19,28)(29,35)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
17 | 116252 | 2 | (1,36)(4,27)(9,41)(16,44)(5,9)(19,28)(29,35)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
17 | 118453 | 3 | (1,36)(4,27)(9,41)(16,44)(5,9)(19,28)(29,35)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
18 | 120609 | 2 | (1,36)(4,27)(9,41)(16,44)(5,9)(19,28)(29,35)(25,24)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
18 | 122958 | 2 | (1,36)(4,27)(9,41)(16,44)(5,9)(19,28)(29,35)(25,24)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
19 | 226364 | 4 | (1,36)(4,27)(9,41)(16,44)(5,9)(19,28)(32,39)(29,35)(25,24)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
19 | 229094 | 6 | (1,36)(4,27)(9,41)(16,44)(5,9)(19,28)(32,39)(29,35)(25,24)(49,45)(49,42)(48,41)(15,12)(37,27)(45,32)(27,16)(25,9)(4,1)(9,2) |
20 | 12454 | 13 | (3,14)(17,41)(4,9)(27,41)(2,3)(5,6)(29,31)(19,20)(34,32)(48,41)(40,32)(48,35)(28,20)(42,29)(49,31)(23,11)(40,17)(13,3)(41,8)(34,1) |
20 | 26500 | 14 | (3,14)(17,41)(4,9)(27,41)(2,3)(5,6)(29,31)(19,20)(34,32)(48,41)(40,32)(48,35)(28,20)(42,29)(49,31)(23,11)(40,17)(13,3)(41,8)(34,1) |
21 | 337507 | 9 | (3,38)(3,14)(17,41)(4,9)(27,41)(2,3)(5,6)(29,31)(19,20)(34,32)(48,41)(40,32)(48,35)(28,20)(42,29)(49,31)(23,11)(40,17)(13,3)(41,8)(34,1) |
21 | 354170 | 19 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
21 | 374810 | 21 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
21 | 398595 | 25 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
21 | 413887 | 16 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 432067 | 19 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 448042 | 17 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 467637 | 21 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 482213 | 17 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 500786 | 21 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 520827 | 21 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
22 | 537811 | 18 | (2,43)(2,31)(8,49)(7,42)(11,33)(20,39)(27,49)(17,30)(28,47)(28,45)(36,45)(29,36)(42,45)(1,1)(46,32)(48,29)(50,29)(32,11)(43,13)(32,4)(24,3)(26,2) |
空间复杂度
输入数据:O(N)
输入N个物品的数据(重量,价值)和背包承载重量等信息
数据预处理:O(N)
根据性价比对物品进行排序:O(N)
根据物品1的性价比计算上界:O(1)
根据所有物品计算下界:O(1)
分支定界法:O(2^(N+1))
最坏情况:O(2^(N+1))
最坏情况是完美二叉树,所以树的高度为n+1(根节点为1),节点总数为2^(N+1)-1。所以空间复杂度为O(2^(N+1))
最好情况:O(N)
最好情况是第一次剪枝就结束,即所有的物品中只有一个物品能被装下。
附录
题解代码
#include<bits/stdc++.h>
using namespace std;
typedef struct Good
{
int weight;
int value;
double profit;
Good(int w, int v)
{
weight = w;
value = v;
profit = (double)v / w;
}
};
bool cmp_Good(const Good& a, const Good& b)
{
return a.profit > b.profit;
}
typedef struct Bag
{
int n;
vector<Good>goods;
double bottom, top;
int MAX_WEIGHT;
void CreatData()
{
srand(time(0));
for (int i = 0; i < n; i++)
{
int w = rand() % 30 + 1, v = rand() % 50 + 1;
goods.push_back(Good(w, v));
}
printf("重量:");
for (int i = 0; i < n; i++)
printf("%d ", goods[i].weight);
printf("\n");
printf("价格:");
for (int i = 0; i < n; i++)
printf("%d ", goods[i].value);
printf("\n");
MAX_WEIGHT = 100;
}
void PretreatmentData()
{
sort(goods.begin(), goods.end(), cmp_Good);
top = MAX_WEIGHT * goods[0].profit;
bottom = 0;
double w = 0;
for (int i = 0; i < goods.size(); i++)
{
if (w + goods[i].weight > MAX_WEIGHT)
break;
bottom += goods[i].value;
w += goods[i].weight;
}
}
Bag(int n)
{
this->n = n;
}
}Bag;
typedef struct node
{
int weight;
int value;
double profit;
int index;
node* parent;
bool operator<(const node& a) const
{
return this->profit > a.profit; //大顶堆
}
node(int w, int v, double p, int index, node* parent)
{
weight = w;
value = v;
profit = p;
this->index = index;
this->parent = parent;
}
}node;
//Branch and bound method分支定界法
node* BABM(Bag bag)
{
node* ans = new node(0, 0, 0, -1, NULL);
//剪枝:当背包内所有物品的重量少于MAX_WEIGHT
int w = 0;
for (int i = 0; i < bag.n; i++)
{
w += bag.goods[i].weight;
}
if (w <= bag.MAX_WEIGHT)
{
for (int i = 0; i < bag.n; i++)
{
int weight = ans->weight + bag.goods[i].weight;
int value = ans->value + bag.goods[i].value;
node* next = new node(weight, value, 0, i, ans);
ans = next;
}
return ans;
}
priority_queue<node*, vector<node*>>q;
q.push(ans);
while (q.size())
{
node* front = q.top();
q.pop();
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (front->profit < ans->value)
continue;
if (front->value > ans->value)
ans = front;
int index = front->index + 1;
if (index >= bag.n-1)
continue;
int w1 = front->weight + bag.goods[index].weight;
int v1 = front->value + bag.goods[index].value;
double p1 = v1 + (bag.MAX_WEIGHT - w1) * bag.goods[index+1].profit;
node* lchild = new node(w1, v1, p1, index, front);
double p2 = front->value + (bag.MAX_WEIGHT - front->weight) * bag.goods[index+1].profit;
node* rchild = new node(front->weight, front->value, p2, index, front);
//剪枝:当超重,舍弃
if (lchild->weight <= bag.MAX_WEIGHT)
q.push(lchild);
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (lchild->profit >= ans->value || lchild->profit < bag.bottom)
q.push(rchild);
}
return ans;
}
void print(node* ans)
{
printf("最大价值:%d\n", ans->value);
printf("最大重量:%d\n", ans->weight);
printf("包中的物品有:");
while (ans->index != -1)
{
printf("%d ", ans->index);
ans = ans->parent;
}
}
Bag input()
{
int n, mode;
cout << "请输入物品的数量:";
cin >> n;
cout << "请选择模式:[0]手动输入 [1]自动生成" << endl;
cin >> mode;
Bag bag(n);
if (!mode)
{
int* weight = new int[n], * value = new int[n];
cout << "请输入物品的价格";
for (int i = 0; i < n; i++)
scanf("%d", &value[i]);
cout << "请输入物品的重量";
for (int i = 0; i < n; i++)
scanf("%d", &weight[i]);
cout << "请输入背包的承载重量";
scanf("%d", &bag.MAX_WEIGHT);
for (int i = 0; i < n; i++)
bag.goods.push_back(Good(weight[i], value[i]));
}
else
bag.CreatData();
return bag;
}
int main()
{
Bag bag = input();
bag.PretreatmentData();
node* ans = BABM(bag);
print(ans);
}
时间测试代码
#include<bits/stdc++.h>
using namespace std;
int cnt = 0;
typedef struct Good
{
int weight;
int value;
double profit;
Good(int w, int v)
{
weight = w;
value = v;
profit = (double)v / w;
}
};
bool cmp_Good(const Good& a, const Good& b)
{
return a.profit > b.profit;
}
typedef struct Bag
{
int n;
vector<Good>goods;
double bottom, top;
int MAX_WEIGHT;
void CreatData()
{
srand(time(0));
int sum = 0;
for (int i = 0; i < n; i++)
{
int w = rand() % 50 + 1, v = rand() % 50 + 1;
goods.push_back(Good(w, v));
sum += w;
}
MAX_WEIGHT = sum * 0.8;
}
void PretreatmentData()
{
sort(goods.begin(), goods.end(), cmp_Good);
top = MAX_WEIGHT * goods[0].profit;
bottom = 0;
double w = 0;
for (int i = 0; i < goods.size(); i++)
{
if (w + goods[i].weight > MAX_WEIGHT)
break;
bottom += goods[i].value;
w += goods[i].weight;
}
}
void print()
{
for (int i = 0; i < n; i++)
{
printf("(%d,%d)", goods[i].weight, goods[i].value);
}
}
Bag(int n)
{
this->n = n;
}
}Bag;
typedef struct node
{
int weight;
int value;
double profit;
int index;
node* parent;
bool operator<(const node& a) const
{
return this->profit > a.profit; //大顶堆
}
node(int w, int v, double p, int index, node* parent)
{
weight = w;
value = v;
profit = p;
this->index = index;
this->parent = parent;
}
}node;
//Branch and bound method分支定界法
node* BABM(Bag bag)
{
node* ans = new node(0, 0, 0, -1, NULL);
//剪枝:当背包内所有物品的重量少于MAX_WEIGHT
int w = 0;
for (int i = 0; i < bag.n; i++)
{
w += bag.goods[i].weight;
}
if (w <= bag.MAX_WEIGHT)
{
for (int i = 0; i < bag.n; i++)
{
int weight = ans->weight + bag.goods[i].weight;
int value = ans->value + bag.goods[i].value;
node* next = new node(weight, value, 0, i, ans);
ans = next;
}
return ans;
}
priority_queue<node*, vector<node*>>q;
q.push(ans);
while (q.size())
{
cnt++;
node* front = q.top();
q.pop();
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (front->profit < ans->value)
{
continue;
}
if (front->value > ans->value)
ans = front;
int index = front->index + 1;
if (index >= bag.n-1)
continue;
int w1 = front->weight + bag.goods[index].weight;
int v1 = front->value + bag.goods[index].value;
double p1 = v1 + (bag.MAX_WEIGHT - w1) * bag.goods[index+1].profit;
node* lchild = new node(w1, v1, p1, index, front);
double p2 = front->value + (bag.MAX_WEIGHT - front->weight) * bag.goods[index+1].profit;
node* rchild = new node(front->weight, front->value, p2, index, front);
//剪枝:当超重,舍弃
if (lchild->weight <= bag.MAX_WEIGHT)
{
q.push(lchild);
}
//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
if (lchild->profit >= ans->value || lchild->profit < bag.bottom)
{
q.push(rchild);
}
}
return ans;
}
int main()
{
printf("物品数量 进入优先队列数量 时间(ms) 数据(重量&价值)\n");
for (int i = 20; i < 40; i++)
{
for (int j = 0; j < 20; j++)
{
cout << i << " ";
clock_t start, end;
start = clock();
Bag bag = Bag(i);
bag.CreatData();
bag.PretreatmentData();
node* ans = BABM(bag);
end = clock(); //结束时间
printf("%d %.4f ", cnt, double(end - start) / CLOCKS_PER_SEC * 1000);
bag.print();
printf("\n");
}
}
}
散点图代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import random
def draw(x, y):
font = matplotlib.font_manager.FontProperties(fname=r'C:\Windows\Fonts\simkai.ttf') # 设置字体
area = 200
colors = [random.random() for i in range(len(x))]
plt.figure(figsize=(20, 10))
plt.style.use('seaborn-whitegrid')
plt.scatter(x, y, s=area, c=colors, alpha=0.4, label='time', cmap='viridis')
# 颜色条:viridis,RdBu
plt.legend(fontsize=30)
plt.xlabel("物品数量", fontsize=30, fontproperties=font)
plt.ylabel('时间', fontsize=30, fontproperties=font)
plt.title("时间散点图", fontsize=30, fontproperties=font)
# plt.xticks([]) # 不显示x轴刻度值
# plt.tick_params(labelsize=20) 刻度值字体大小
plt.tick_params(pad=5) # 刻度距离坐标轴的距离调整
plt.colorbar() # 显示颜色对比条
plt.savefig(r"D:\Desktop\a.png", bbox_inches='tight', dpi=1000)
plt.show()
if __name__ == "__main__":
path = r"D:\Desktop\time.txt"
data = open(path).readlines()
x, y = [], []
for i in data:
a, b = i.split(' ')
x.append(int(a))
y.append(float(b))
draw(x, y)