算法设计与分析——使用回溯法实现0-1背包问题——回溯法的基本回顾

问题描述
  • 题目描述:
    有4个物品,其重量分别是{2, 3, 4, 5},价值分别为{3, 4, 5, 6},背包的容量为8。如何装才能价值最大,最大价值为多少?

  • 输入格式:
    第一行:是测试数据。
    接下来对于每组测试数据,第一行是物品数量和背包最大承重,第二行是每个物品的价值,第三行是每个物品的重量。

  • 输出格式:
    一个整数,为最大价值总和是多少。

  • 输入样例:

      4 8
      3 4 5 6
      2 3 4 5
    
  • 输出样例:

      10
    
回溯算法的回顾
  • 概述:回溯算法是一种通用的解题法,有较广的适用性
  • 基本步骤:
    • 定义问题的解空间,解空间必须包括问题的最优解,一般情况下要列出所有的解
    • 确定易于搜索的解空间结构:常用的有两种,子集树(零一背包问题)和排列树(货郎问题)
      * 子集树:从n个元素的集合S中找出满足某种性质的子集时
      * 排列树:从n个元素的集合S中找出满足某种性质的排列时
    • 从根节点出发,以深度优先的方式去搜索解空间树
    • 确定剪枝函数,两种策略:约束函数和限界函数
使用知识回顾去解决问题
定义问题的解空间
  • 解空间必须包含的问题的最优解的
  • 一般情况下,都是列出所有的解
    在这里插入图片描述
确定易于搜索的解空间结构
  • 定义问题的解空间,每一个物品有两种状态,放或者不放,形成对应的树形结构如图。

单个物体
在这里插入图片描述
多个物体

在这里插入图片描述

  • 因为每一个物体只有放或者不放两种状态,就是一个二叉树,这里只列了三层,实际上有四层,每一个结点的值是当前物体的价值。
  • 其实,我知道表示成了二叉树,但是并不知道如何使用代码去实现。使用二叉树的数据结构吗?链表还是数组。
从根节点出发以深度优先的方式搜索空间树
  • 从根节点出发以深度优先的方式搜索解空间树。算法搜索至空间树的任意一点时,先判断该点是否包含问题的解。
  • 判断依据:
    * 肯定不包含解,即重量超过背包的容量,则跳过对该节点为根的子树的搜索,逐层向祖先结点回溯
    * 肯定包含解,即重量没有超过背包的容量,则继续按照深度优先搜索的策略进行搜索。
    在这里插入图片描述
实现代码
#include<iostream>
#include<math.h>

using namespace std;

//用于记录是否存放当前地物体
int inOut[4];
//保存最多的价值
int value;
//定义背包的总共的重量的
int bagVolume = 9;

/*
	描述:背包问题的约束条件,当传入对应的序号,就去判定是否要放对应的物品
	参数:放入包中物体的序号
	返回:当前物体总重量和背包容量的关系
		 true:表示没有超重
		 false:表示超重
	原理:判定当前的物品的总重量,是不是小于物体的实际重量
*/
bool bagConstraint(int m, int weight[]) {
	//一直遍历m层之前的所有物体,求出其对应的重量
	int allweight = 0;
	for (int i = 0; i <= m; ++i)
	{
		//计算出总共的重量的
		allweight += inOut[i] * weight[i];
	}

	//比较当前的物体总重量和背包的总重量关系
	return allweight <= bagVolume;
}

/*
	描述:深度优先搜索的函数,递归函数
	参数:m:是要装入背包的物品的数量
		 weight:是背包中各个物品的重量
		 value:是背包中各个物品的价值
	返回:最终返回的是最大的价值
	问题:
*/
void bagProblem(int m, int weight[], int valueAll[]) {

	//首先确定终止条件,那就比较最大值
	if (m == 4)
	{
		int sum = 0;
		for (int i = 0; i < m; ++i)
		{
			sum += valueAll[i] * inOut[i];
		}

		//比较最大值
		if (sum > value)
		{
			value = sum;
		}
	}
	else {
		//没有到达终止条件,继续向下进行递归
		for (int i = 0; i < 2; ++i)
		{
			inOut[m] = i;
			//判定是否满足对应约束条件
			if (bagConstraint(m, weight))
			{
				//满足约束条件,继续向下进行递归的
				bagProblem(m + 1, weight, valueAll);
			}
		}

	}

}

int main(int argc, char const* argv[])
{
	cout << "输入的样例:物品数量:4" << endl;
	cout << "背包容量:9" << endl;
	cout << "重量分别是:2 3 4 5" << endl;
	cout << "价值分别是:3 4 5 6 " << endl;
	int num = 4;
	int weight[4] = { 2,3,4,5 };
	int valueAll[4] = { 3,4,5,6 };
	bagProblem(0, weight, valueAll);
	cout << "最终的结果是:" << value << endl;
	cout << endl;
	return 0;

}