两道回溯法的例题

1.重写0-1背包问题的回溯法,使算法能输出最优解

[算法描述]

0-1背包问题是子集选取问题。一般情况下,0-1背包问题是NP完全问题。0-1背包问题的解空间可以用子集树表示。解0-1背包问题的回溯法与解装载问题的回溯法十分相似。在搜索解空间树时,只要其左儿子节点是一个可行的节点,搜索就进入其左子树;而当右子树中有可能包含最优解时才进入右子树搜索否则将右子树剪去。设r是当前剩余物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r<=bestp时,可剪去右子树。计算右子树中解的上界的更好的办法是,将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包,由此得到的价值是右子树的上界

#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;

}

2.最大团问题回溯法求解

完全图:任意两点都恰有一条边相连的图(任意两点都相邻)。

完全子图:满足任意两点都恰有一条边相连的子图,也叫团。

最大完全子图:所有完全子图中顶点数最大的团,即最大团。

#include <iostream>
using namespace std;
int m[101][101];//有向图的邻接矩阵
int x[101];//当前团的解
int bestx[101];//最优解
int n;//表示图的顶点数
int cn=0;//当前团的大小
int bestn;//当前最优值
void getbestn(int i)
{
    if(i>n){//递归出口,到根节点时,更新最优值和最优解,返回
        bestn=cn;//更新最优值
        for(int j=1;j<=n;j++)
            bestx[j]=x[j];
        return ;//返回
    }
    x[i]=1;//先假定x[i]=0;
    for(int j=1;j<i;j++){
        if(x[j]==1&&m[i][j]==0){
            x[i]=0;//如果该点与已知解中的点无边相邻
            break;//则不遍历左子树
        }
    }
    if(x[i]==1){//当且仅当x[i]==1时,遍历左子树
        cn++;//该点加入当前解
        getbestn(i+1);//递归调用
        cn--;//还原当前解
    }
    x[i]=0;//假定x[i]==0
    if(cn+n-i>bestn){//如果当前值+右子树可能选择的节点<当前最优解,不遍历左子树
        x[i]=0;
        getbestn(i+1);
    }
    return ;
}
int main()
{
    cin>>n;//输入图的顶点数
    //输入图的邻接矩阵
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        cin>>m[i][j];
    //求最优解
    getbestn(1);
    cout<<bestn<<endl;//输出最优值
    //输出
    for(int i=1;i<=n;i++)
        if(bestx[i])
        cout<<i<<" ";//输出最优解
    return 0;
}