算法设计-分治定界法

一、目的

1、题目

0-1背包问题:有5个商品,重量分别为816211712,价值分别为81416117,背包的载重量为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 &lt;&lt; "请输入物品的数量:";
	cin &gt;&gt; n;
	cout &lt;&lt; "请选择模式:[0]手动输入 [1]自动生成" &lt;&lt; endl;
	cin &gt;&gt; mode;

	Bag bag(n);

	if (!mode)
	{
		int* weight = new int[n], * value = new int[n];

		cout &lt;&lt; "请输入物品的价格";
		for (int i = 0; i &lt; n; i++)
			scanf("%d", &amp;value[i]);
		cout &lt;&lt; "请输入物品的重量";
		for (int i = 0; i &lt; n; i++)
			scanf("%d", &amp;weight[i]);
		cout &lt;&lt; "请输入背包的承载重量";
		scanf("%d", &amp;bag.MAX_WEIGHT);

		for (int i = 0; i &lt; 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]自动生成" &lt;&lt; endl;
	Read : mode;

	Bag bag(n);

	if (!mode)
	{
		int* weight = new int[n], * value = new int[n];

		Print : "请输入物品的价格";
		for i &lt;- 0 To n
			Read : "%d", &amp;value[i];
		Print : "请输入物品的重量";
		for i &lt;- 0 To n
			Read : "%d", &amp;weight[i];
		Print : "请输入背包的承载重量";
		Read : "%d", &amp;bag.MAX_WEIGHT;

		for i &lt;- 0 To n
			bag.goods.push_back(Good(weight[i], value[i]));
	\}
	Else
		bag.CreatData();

	return bag;
\}

2. 数据预处理

代码:

typedef struct Bag
{
	int n;
	vector&lt;Good&gt;goods;
	double bottom, top;
	int MAX_WEIGHT;
	void CreatData()
	{
		srand(time(0));
		for (int i = 0; i &lt; n; i++)
		{
			int w = rand() % 30 + 1, v = rand() % 50 + 1;
			goods.push_back(Good(w, v));
		}

		printf("重量:");
		for (int i = 0; i &lt; n; i++)
			printf("%d ", goods[i].weight);
		printf("\n");

		printf("价格:");
		for (int i = 0; i &lt; 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 &lt; goods.size(); i++)
		{
			if (w + goods[i].weight &gt; MAX_WEIGHT)
				continue;
			bottom += goods[i].value;
			w += goods[i].weight;
		}
	}
	Bag(int n)
	{
		this-&gt;n = n;
	}
}Bag;

伪代码:

typedef struct Bag
{
	n : integer;
	vector&lt;Good&gt;goods;
	bottom, top : double;
	MAX_WEIGHT : integer;
	void CreatData()
	{
		srand(time(0));
		for i &lt;- 0 To n
		{
			w &lt;- rand() % 30 + 1, v &lt;- rand() % 50 + 1 : integer;
			goods.push_back(Good(w, v));
		}

		Print : "重量:";
		for i &lt;- 0 To n
			Print : "%d ", goods[i].weight;
		Print : "\n";

		Print : "价格:";
		for i &lt;- 0 To n
			Print : "%d ", goods[i].value;
		Print : "\n";

		MAX_WEIGHT &lt;- 100;
	}
	void PretreatmentData()
	{
		sort(goods.begin(), goods.end(), cmp_Good);
		top &lt;- MAX_WEIGHT * goods[0].profit;
		bottom &lt;- 0;
		w &lt;- 0 : double;
		for i &lt;- 0 To goods.size()
		{
			if (w + goods[i].weight &gt; MAX_WEIGHT)
				continue;
			bottom += goods[i].value;
			w += goods[i].weight;
		}
	}
	Bag(n)
	{
		this-&gt;n &lt;- 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&lt;(const node&amp; a) const
	{
		return this-&gt;profit &gt; a.profit; //大顶堆
	}

	node(int w, int v, double p, int index, node* parent)
	{
		weight = w;
		value = v;
		profit = p;
		this-&gt;index = index;
		this-&gt;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 &lt; bag.n; i++)
	{
		w += bag.goods[i].weight;
	}
	if (w &lt;= bag.MAX_WEIGHT)
	{
		for (int i = 0; i &lt; bag.n; i++)
		{
			int weight = ans-&gt;weight + bag.goods[i].weight;
			int value = ans-&gt;value + bag.goods[i].value;
			node* next = new node(weight, value, 0, i, ans);
			ans = next;
		}
		return ans;
	}

	priority_queue&lt;node*, vector&lt;node*&gt;&gt;q;
	q.push(ans);
	while (q.size())
	{
		node* front = q.top();
		q.pop();

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (front-&gt;profit &lt; ans-&gt;value)
			continue;

		if (front-&gt;value &gt; ans-&gt;value)
			ans = front;

		int index = front-&gt;index + 1;
		if (index &gt;= bag.n)
			continue;

		int w1 = front-&gt;weight + bag.goods[index].weight;
		int v1 = front-&gt;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-&gt;value + (bag.MAX_WEIGHT - front-&gt;weight) * bag.goods[index+1].profit;
		node* rchild = new node(front-&gt;weight, front-&gt;value, p2, index, front);

		//剪枝:当超重,舍弃
		if (lchild-&gt;weight &lt;= bag.MAX_WEIGHT)
			q.push(lchild);

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (lchild-&gt;profit &gt;= ans-&gt;value || lchild-&gt;profit &lt; bag.bottom)
			q.push(rchild);
	}
	return ans;
}

伪代码:

function BABM(Bag bag)
{
	node* ans &lt;- new node(0, 0, 0, -1, NULL);
    
	//剪枝:当背包内所有物品的重量少于MAX_WEIGHT
	w &lt;- 0 : integer;
	for i &lt;- 0 To bag.n
		w += bag.goods[i].weight;
	
    if (w &lt;= bag.MAX_WEIGHT)
	{
		for i &lt;- 0 To bag.n
		{
			weight &lt;- ans-&gt;weight + bag.goods[i].weight;
			value &lt;- ans-&gt;value + bag.goods[i].value;
			node* next &lt;- new node(weight, value, 0, i, ans);
			ans &lt;- next;
		}
		return ans;
	}

	priority_queue&lt;node*, vector&lt;node*&gt;&gt;q;
	q.push(ans);
	while (q.size())
	{
		node* front &lt;- q.top();
		q.pop();

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (front-&gt;profit &lt; ans-&gt;value)
			continue;

		if (front-&gt;value &gt; ans-&gt;value)
			ans &lt;- front;

		index &lt;- front-&gt;index + 1 : integer;
		if (index &gt;= bag.n)
			continue;

		w1 &lt;- front-&gt;weight + bag.goods[index].weight : integer;
		v1 &lt;- front-&gt;value + bag.goods[index].value : integer;
		p1 &lt;- v1 + (bag.MAX_WEIGHT - w1) * bag.goods[index].profit : double;
		node* lchild &lt;- new node(w1, v1, p1, index, front);
		p2 &lt;- front-&gt;value + (bag.MAX_WEIGHT - front-&gt;weight) * bag.goods[index].profit : double;
		node* rchild &lt;- new node(front-&gt;weight, front-&gt;value, p2, index, front);

		//剪枝:当超重,舍弃
		if (lchild-&gt;weight &lt;= bag.MAX_WEIGHT)
			q.push(lchild);

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (lchild-&gt;profit &gt;= ans-&gt;value || lchild-&gt;profit &lt; 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-&gt;value);
	printf("最大重量:%d\n", ans-&gt;weight);
	printf("包中的物品有:");
	while (ans-&gt;index != -1)
	{
		printf("%d ", ans-&gt;index);
		ans = ans-&gt;parent;
	}
}

伪代码:

function print(node* ans)
{
    Print : "最大价值:%d\n", ans-&gt;value;
    Print : "最大重量:%d\n", ans-&gt;weight;
    Print : "包中的物品有:";
    while (ans-&gt;index != -1)
    {
        Print : "%d ", ans-&gt;index;
        ans &lt;- ans-&gt;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&lt;bits/stdc++.h&gt;
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&amp; a, const Good&amp; b)
{
	return a.profit &gt; b.profit;
}


typedef struct Bag
{
	int n;
	vector&lt;Good&gt;goods;
	double bottom, top;
	int MAX_WEIGHT;
	void CreatData()
	{
		srand(time(0));
		for (int i = 0; i &lt; n; i++)
		{
			int w = rand() % 30 + 1, v = rand() % 50 + 1;
			goods.push_back(Good(w, v));
		}

		printf("重量:");
		for (int i = 0; i &lt; n; i++)
			printf("%d ", goods[i].weight);
		printf("\n");

		printf("价格:");
		for (int i = 0; i &lt; 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 &lt; goods.size(); i++)
		{
			if (w + goods[i].weight &gt; MAX_WEIGHT)
				break;
			bottom += goods[i].value;
			w += goods[i].weight;
		}
	}
	Bag(int n)
	{
		this-&gt;n = n;
	}
}Bag;


typedef struct node
{
	int weight;
	int value;
	double profit;
	int index;
	node* parent;
	bool operator&lt;(const node&amp; a) const
	{
		return this-&gt;profit &gt; a.profit; //大顶堆
	}

	node(int w, int v, double p, int index, node* parent)
	{
		weight = w;
		value = v;
		profit = p;
		this-&gt;index = index;
		this-&gt;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 &lt; bag.n; i++)
	{
		w += bag.goods[i].weight;
	}
	if (w &lt;= bag.MAX_WEIGHT)
	{
		for (int i = 0; i &lt; bag.n; i++)
		{
			int weight = ans-&gt;weight + bag.goods[i].weight;
			int value = ans-&gt;value + bag.goods[i].value;
			node* next = new node(weight, value, 0, i, ans);
			ans = next;
		}
		return ans;
	}

	priority_queue&lt;node*, vector&lt;node*&gt;&gt;q;
	q.push(ans);
	while (q.size())
	{
		node* front = q.top();
		q.pop();

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (front-&gt;profit &lt; ans-&gt;value)
			continue;

		if (front-&gt;value &gt; ans-&gt;value)
			ans = front;

		int index = front-&gt;index + 1;
		if (index &gt;= bag.n-1)
			continue;

		int w1 = front-&gt;weight + bag.goods[index].weight;
		int v1 = front-&gt;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-&gt;value + (bag.MAX_WEIGHT - front-&gt;weight) * bag.goods[index+1].profit;
		node* rchild = new node(front-&gt;weight, front-&gt;value, p2, index, front);

		//剪枝:当超重,舍弃
		if (lchild-&gt;weight &lt;= bag.MAX_WEIGHT)
			q.push(lchild);

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (lchild-&gt;profit &gt;= ans-&gt;value || lchild-&gt;profit &lt; bag.bottom)
			q.push(rchild);
	}
	return ans;
}

void print(node* ans)
{
	printf("最大价值:%d\n", ans-&gt;value);
	printf("最大重量:%d\n", ans-&gt;weight);
	printf("包中的物品有:");
	while (ans-&gt;index != -1)
	{
		printf("%d ", ans-&gt;index);
		ans = ans-&gt;parent;
	}
}

Bag input()
{
	int n, mode;
	cout &lt;&lt; "请输入物品的数量:";
	cin &gt;&gt; n;
	cout &lt;&lt; "请选择模式:[0]手动输入 [1]自动生成" &lt;&lt; endl;
	cin &gt;&gt; mode;

	Bag bag(n);

	if (!mode)
	{
		int* weight = new int[n], * value = new int[n];

		cout &lt;&lt; "请输入物品的价格";
		for (int i = 0; i &lt; n; i++)
			scanf("%d", &amp;value[i]);
		cout &lt;&lt; "请输入物品的重量";
		for (int i = 0; i &lt; n; i++)
			scanf("%d", &amp;weight[i]);
		cout &lt;&lt; "请输入背包的承载重量";
		scanf("%d", &amp;bag.MAX_WEIGHT);

		for (int i = 0; i &lt; 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&lt;bits/stdc++.h&gt;
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&amp; a, const Good&amp; b)
{
	return a.profit &gt; b.profit;
}


typedef struct Bag
{
	int n;
	vector&lt;Good&gt;goods;
	double bottom, top;
	int MAX_WEIGHT;
	void CreatData()
	{
		srand(time(0));
		int sum = 0;
		for (int i = 0; i &lt; 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 &lt; goods.size(); i++)
		{
			if (w + goods[i].weight &gt; MAX_WEIGHT)
				break;
			bottom += goods[i].value;
			w += goods[i].weight;
		}
	}
	void print()
	{
		for (int i = 0; i &lt; n; i++)
		{
			printf("(%d,%d)", goods[i].weight, goods[i].value);
		}
	}
	Bag(int n)
	{
		this-&gt;n = n;
	}
}Bag;


typedef struct node
{
	int weight;
	int value;
	double profit;
	int index;
	node* parent;
	bool operator&lt;(const node&amp; a) const
	{
		return this-&gt;profit &gt; a.profit; //大顶堆
	}

	node(int w, int v, double p, int index, node* parent)
	{
		weight = w;
		value = v;
		profit = p;
		this-&gt;index = index;
		this-&gt;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 &lt; bag.n; i++)
	{
		w += bag.goods[i].weight;
	}
	if (w &lt;= bag.MAX_WEIGHT)
	{
		for (int i = 0; i &lt; bag.n; i++)
		{
			int weight = ans-&gt;weight + bag.goods[i].weight;
			int value = ans-&gt;value + bag.goods[i].value;
			node* next = new node(weight, value, 0, i, ans);
			ans = next;
		}
		return ans;
	}

	priority_queue&lt;node*, vector&lt;node*&gt;&gt;q;
	q.push(ans);
	
	while (q.size())
	{
		cnt++;
		node* front = q.top();
		q.pop();

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (front-&gt;profit &lt; ans-&gt;value)
		{
			continue;
		}

		if (front-&gt;value &gt; ans-&gt;value)
			ans = front;

		int index = front-&gt;index + 1;
		if (index &gt;= bag.n-1)
			continue;

		int w1 = front-&gt;weight + bag.goods[index].weight;
		int v1 = front-&gt;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-&gt;value + (bag.MAX_WEIGHT - front-&gt;weight) * bag.goods[index+1].profit;
		node* rchild = new node(front-&gt;weight, front-&gt;value, p2, index, front);

		//剪枝:当超重,舍弃
		if (lchild-&gt;weight &lt;= bag.MAX_WEIGHT)
		{
			q.push(lchild);
		}

		//剪枝:当该策略的价格上界少于当前最优解的价格,舍弃
		if (lchild-&gt;profit &gt;= ans-&gt;value || lchild-&gt;profit &lt; bag.bottom)
		{
			q.push(rchild);
		}
	}
	return ans;
}

int main()
{
	printf("物品数量	进入优先队列数量	时间(ms)    数据(重量&amp;价值)\n");
	for (int i = 20; i &lt; 40; i++)
	{
		for (int j = 0; j &lt; 20; j++)
		{
			cout &lt;&lt; i &lt;&lt; " ";
			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)
posted @ 2023-08-25 12:01  jijfurhg  阅读(76)  评论(0)    收藏  举报