算法设计-分治定界法

一、目的

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

typedef struct Bag
{
    int n;
    vector<Good>goods;
    double bottom,top;
    int MAX_WEIGHT;
}

释义:背包

属性:

物品数量n

物品goods

上界top与下界bottom

最大可承载重量MAX_WEIGHT

Good

typedef struct Good
{
    int weight;
    int value;
    double profit;
}

释义:物品

属性:

重量weight,价格value,性价比profit

node

typedef struct node
{
    int weight;
    int value;
    double profit;
    int index;
    node* parent;
}

释义:优先队列的节点

属性:

此时背包的重量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;

预处理结果:

序号重量价格性价比
1881
216140.875
321160.761
417110.647
51270.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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值