桂 林 理 工 大 学
实 验 报 告
班级 软件工程16-1班 学号 3162052051116 姓名 张识虔 同组实验者
实验名称 回溯法 日期 2018年 11 月14 日
一、实验目的与要求:
1、通过回溯法的示例程序理解回溯法的基本思想;
2、运用回溯法解决实际问题进一步加深对回溯法的理解和运用;
二、实验内容:
1、分析并掌握“0—1背包” 问题的回溯算法求解方法;
2、 编程实现回溯法的典型例题。
三、实验步骤
1.理解回溯算法思想和算法示例;
2.上机输入和调试算法示例程序;
3.理解实验题的问题要求;
4.上机输入和调试自己所编的实验题程序;
5.验证并分析实验题的实验结果;
6.整理出实验报告;
四、示例程序1: 0-1背包问题
0-1背包:给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得在总重量不超过背包的容量C的前提下装入背包中物品的总价值最大。
源代码见Knapsack.cpp, Knapsack_bound.cpp
五、实验题
1. 教材210页习题9第5题。(也就是子集和问题)
给定一个正整数集合X={x1,x2,….,xn}和一个正整数y,设计回溯算法,求集合X的一个子集Y,使得中元素之和等于y
#include<iostream>
using namespace std;
#define NUM 10000
int n;
int c;
int cw;
int bestw;
int w[NUM];
int x[NUM];
int r;
bool flag;
void backtrack(int t)
{
if(t>n)
{
if(cw==c)
{
for(int i=1; i<=n; i++){
if (x[i]){
cout<<w[i]<<" ";
}
}
flag = false;
cout<<endl<<endl;
}
return;
}
r -= w[t];
if (cw+w[t]<=c)
{
x[t] = 1;
cw += w[t];
backtrack(t+1);
cw -= w[t];
}
if (cw+r>bestw)
{
x[t] = 0;
backtrack(t+1);
}
r += w[t];
}
int main()
{
cout<<"输入n个数,输入c被求和"<<" 输入 0 0 结束程序"<<endl;
while(scanf("%d%d",&n,&c) && (n||c))
{
r = 0;
cout<<"输入"<<n<<"个数字"<<endl;
for(int i=1; i<=n; i++)
{
cin>>w[i];
r += w[i];
}
cw = 0;
bestw = 0;
flag = true;
if(flag){
cout<<endl<<"输出组合方式"<<endl;
}
backtrack(1);
if (flag)
cout<<"没有答案"<<endl;
}
return 0;
}
2. 整数变换问题:整数i的两种变换定义为,(向下取整);设计一个算法求给定两个整数a和b,用最少次数的和变换将整数a变换为b;例如
题目分析:
观察f和g两个函数发现,f总是使得自变量x变大,g总是使得自变量x变小。因此我们在决定让x执行哪个函数之前必须先判断x和目标值m之间的大小关系。如果x>m,就让其执行g函数;反之,执行f函数。
这道题目有两种情况,一种是有解,即n可以通过函数变换成m;另一种是无解,即n无法通过函数变换成m。第一种情况比较容易,即只需要判断最后的x是否等于m即可。如果x等于m,那么说明n已经被变换成m了,递归返回。第二种情况,如果在递归的过程,出现了前面计算过的元素,那就说明n是无法转换成m的。
用回溯法解决整数变换问题,用子集树。
剪枝条件:
显示约束:如果x>m,就剪掉它的左子树;如果x<m,就剪掉它的右子树;
隐式约束:如果在某次计算的过程中发现当前的计算次数已经大于或等于最少计算次数了,就剪掉这个分支。
#include <iostream>
using namespace std;
#define max 10
int n,m;
int k;
bool goal;
int road[max+1]; //用于记录g f 排列信息
int Fx(int t,int i)
{
if(i==0) //i用来标记 函数种类
return t/2; //i=0 f(t)=t/2;
else
return 3*t; //i=1 f(t)=3*t;
}
bool Search(int dep,int N)
{
if(dep>k) return false; //深度不能大于k
for(int i=0;i<2;i++)
{
int num=N;
num=Fx(num,i);
road[dep]=i;
if(num==m||Search(dep+1,num))
{
goal=true;
return true;
}
}
return false;
}
void Procedure()
{
k=1;
goal=false;
while(!Search(1,n)) //回溯
{
k++;
if(k>max)
break;
if(goal)
break;
}
}
int main()
{
cout<<"\t输入要求变换的数n:";
cin>>n;
cout<<endl;
cout<<"\t输入要求变换成什么数m:";
cin>>m;
cout<<endl;
for(int i=0;i<max;i++)
{
road[i]=0;
}
Procedure();
if(goal)
{
cout<<"\t"<<n<<" 可以变换成 "<<m<< ",并且得到的最小变换次数为:";
cout<<k<<endl<<endl;
cout<<"\t得到的结果为 "<<m<<" = ";
for(int i=1;i<=k;i++)
{
if(road[i]==0){
cout<<" "<<"g";
}
else if(road[i]==1){
cout<<" "<<"f";
}
}
cout<<"("<<n<<")";
}
else
cout<<"\t没有结果!"<<endl;
return 0;
}