两道回溯法的例题
两道回溯法的例题
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;
}