回溯法-子集树递归树-装载问题

回溯法

深度优先策略(回忆深度优先遍历二叉树思路)
解题步骤:

 1)针对所给问题,定义问题的解空间;例如,n个物品的0-1背包问题所对应的解空间树是一棵子集树。
 2)确定易于搜索的解空间结构;
 3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数(****约束函数除去不满足约束的子树,限界函数减去得不到最优解的子树**)**避免无效搜索

## 子集树和递归树

扩展结点:一个正在产生儿子的结点称为扩展结点。
活结点:一个自身已生成但其儿子还没有全部生成的节点称做活结点。
死结点:一个所有儿子已经产生的结点称做死结点。
1.当所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间称为子集树
0-1背包问题

//回溯法遍历子集树
void Traceback(int k)//k为扩展结点在解空间树中所处的层次
{
    if (k > n)//n标识问题的规模
        output(x);//存放当前解的一维数组
    if (constraint(k))//约束条件
    {
        //相关标识
        backtrack(k + 1);
        //相关标识的反操作---->从左子树退回,考虑从父节点的角度(场景复原)
    }
    if (bound(k))//限定函数
    {
        //相关标识
        backtrack(k + 1);
        //相关标识的反操作、、、、?
    }
    //或者:  
// //for (int i=0;i<=1;i++) {   //控制分支的数目,此处只有两个分支,0、1分别代表是否装入背包
	x[t] = i;
	if (constraint(t) && bound(t))
			// backtrack(t + 1); 
			//剪枝函数:约束函数+限界函数 ——> 递归
       }

       ————————————————
           版权声明:本文为CSDN博主「有梦想的小树」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
           原文链接:https ://blog.csdn.net/m0_38109046/article/details/84844640
//}
}

2.当所给问题是确定n个元素满足某种性质的排列时,相应的解空间树称为 排列树

//回溯法遍历排列树
//void backtrack(int t)//扩展结点在解空间树中所处的层次
//{
//    if (t > n)//n:规模
//        output(x);//存放当前解的一维数组
//    else {
//        for (int i = t; i <= n; i++)
//一条12345路径,我们从1出发1->2->3->4->5 那我们backtrace(2)的时候会把2和345交换位置,就是为了获得1->3, 1->4, 1->5开头的路径。
//        {
//            swap(x[t], x[i]);              //实现两个位置的交换
//            if (constraint(t) && bound(t))   //约束函数与限定函数
//                backtrack(t + 1)                //递归
//            swap(x[t], x[i]);              //恢复原状

//全排列Perm算法同样用到该思路//回看前期文章
//        }
//    }
//}
// ZhuangZaiWenTi!.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//回溯法:搜索,避免不必要的搜索
 基本思路: 如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。
//(1)首先将第一艘轮船尽可能装满;
//(2)将剩余的集装箱装上第二艘轮船。
//将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近C1。由此可知,装载问题等价于以下特殊的0 - 1背包问题 。
//————————————————
//版权声明:本文为CSDN博主「one or only」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https ://blog.csdn.net/qq_37308779/article/details/104758870
约束函数+上限函数


#include <iostream>
using namespace std;

template <class Type>
class Loading
{
	friend Type MaxLoading(Type[],Type,int,int []);
	//private:
public:
	void Backtrack(int i);
	int n,			//集装箱数
		* x,			//当前解
		* bestx;		//当前最优解
	Type* w,	//集装箱重量数组
		c,			//第一艘轮船的载重量
		cw,			//当前载重量
		bestw,		//当前最优载重量
		r;          //剩余集装箱重量
};

template <class Type>
void  Loading <Type>::Backtrack(int i);

template<class Type>
Type MaxLoading(Type w[], Type c, int n, int bestx[]);

int main()
{
	int n = 3, m;
	int c = 50, c2 = 50;

	int w[4] = { 0,10,40,40 };
	int bestx[4];
	//main输入信息作为类中函数的参数
	m = MaxLoading(w, c, n, bestx);

	cout << "轮船的载重量分别为:" << endl;
	cout << "c(1)=" << c << ",c(2)=" << c2 << endl;

	cout << "待装集装箱重量分别为:" << endl;
	cout << "w(i)=";
	for (int i = 1; i <= n; i++)
	{
		cout << w[i] << " ";
	}
	cout << endl;

	cout << "回溯选择结果为:" << endl;
	cout << "m(1)=" << m << endl;//最优装载重量
	cout << "x(i)=";
	for (int i = 1; i <= n; i++)
	{
		cout << bestx[i] << " ";//当前最优解,类属性!!!!!!
	}
	cout << endl;

	int m2 = 0;
	for (int j = 1; j <= n; j++)
	{
		m2 = m2 + w[j] * (1 - bestx[j]);//放入二号轮船的重量和
	}
	cout << "m(2)=" << m2 << endl;

	if (m2 > c2)
	{
		cout << "因为m(2)大于c(2),所以原问题无解!" << endl;
	}
	return 0;
}

template <class Type>
void  Loading <Type>::Backtrack(int i)// 搜索第i层结点
{
	if (i > n)//大于物品数
	{
		if (cw > bestw)//当前最优载重量
		//bestw只能在到达叶子结点的时候更新,最终会留下最优的解!!!!!不同于回溯法0-1背包问题到达结点的判断!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		{
			for (int i= 1; i <= n; i++)
			{
				bestx[i] = x[i];//将当前解给最优解
				bestw = cw;
			}
		}
		return;
	}

	//i层结点考虑了,那么从r中出去
	r -= w[i];
	//考虑放入在一号轮船,作为左子树
	if (cw + w[i] <= c)//c为剩余载重
	{
		cw += w[i];
		x[i] = 1;
		//c -= w[i];//错了!!!不需要变化c,cw一直在变化!!
		Backtrack(i + 1);
		//c += w[i];
		cw -= w[i];
	}

	//考虑放入二号,作为右子树
	if (cw + r > bestw)//有可能存在最优解才会去访问
	{
		x[i] = 0;
		Backtrack(i + 1);
	}
	
	//考虑完i层后,返回上一层,那么对于上一层而言,其r、cw要恢复回去
	r += w[i];
}

//返回最优载重
template<class Type>
Type MaxLoading(Type w[], Type c, int n, int bestx[])//返回最优载重量
{
	//初始化!!!!
	Loading<Type>X;
	//初始化X
	X.x = new int[n + 1];
	X.w = w;
	X.c = c;
	X.n = n;
	X.bestx = bestx;
	X.bestw = 0;
	X.cw = 0;
	//初始化r
	X.r = 0;

	for (int i = 1; i <= n; i++)
	{
		X.r += w[i];
	}

	//类对象调用类函数,其余属性都方便调用!!!
	//1开始:回溯搜索
	X.Backtrack(1);
	delete[]X.x;
	//返回最优结果
	return X.bestw;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值