实验五、回溯法

实验五、回溯法
 一、实验目的与要求:
1、通过回溯法的示例程序理解回溯法的基本思想;
2、运用回溯法解决实际问题进一步加深对回溯法的理解和运用;

二、实验内容:
1、分析并掌握“符号三角” 问题的回溯法求解方法;
2、分析并掌握“n皇后” 问题的回溯算法求解方法;
3、练习使用回溯法求解“整数变换”等问题。

三、实验步骤
1.理解回溯算法思想和算法示例;
2.上机输入和调试算法示例程序;
3.理解实验题的问题要求;
4.上机输入和调试自己所编的实验题程序;
5.验证并分析实验题的实验结果;
6.整理出实验报告;

四、示例程序1:符号三角形问题
符号三角问题:下面都是“-”。 下图是由14个“+”和14个“-”组成的符号三角形。2个同号下面都是“+”,2个异号下面都是“-”。
+   +   -   +   -   +   +
+   -   -   -   -   +
-   +   +   +   -
   -   +   +   -
   -   +   -
   -   -
   +
在一般情况下,符号三角形的第一行有n个符号。符号三角形问题要求对于给定的n,计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。
参考代码见教材134页。请在此基础上,写出主函数,并运行程序。

示例程序2:n皇后问题
n皇后问题:在N行N列的棋盘上放置不能互相攻击的N个皇后。根据国际象棋规则,处在同一行或同一列或同一斜线上的皇后可以互相攻击。N皇后问题的解满足以下条件:棋盘每行均放置一个皇后,且任何2个皇后不在同一列,也不在同一斜线上。
参考代码1:
#include <stdio.h>
#include <math.h>
class Queen
{
friend int nQueen(int);
private:
bool Place(int k);
void Backtrack(int t);
int n,*x;
long sum;
};

bool Queen::Place(int k)
{
for (int j=1;j<k; j++)
if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))
return false;
return true;
}

void Queen::Backtrack(int t)
{
if (t>n)
{
sum++;
}
else
{
for (int i=1; i<=n; i++)
{
x[t]=i;
if (Place(t))
Backtrack(t+1);
}
}
}

int nQueen(int n)
{
Queen X;
X.n=n;
X.sum=0;
int *p=new int[n+1];
for (int i=0;i<=n; i++)
p[i]=0;
X.x=p;
X.Backtrack(1);
delete[] p;
return X.sum;
}

int main()
{
int x,y;
x=8;
y=nQueen(x);
printf(“sum=%d\n”,y);
return 0;
}

参考代码2:
#include <stdio.h>
#include <math.h>>
class Queen
{
friend int nQueen(int);

private:
bool Place(int k);
void Backtrack(int t);
void swap(int *a,int *b);
int n,*x;
long sum;
};

void Queen::swap(int *a,int *b)
{
int temp;
temp=*a; *a=*b; *b=temp;
}

bool Queen::Place(int k)
{
for (int j=1;j<k; j++)
if ((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))
return false;
return true;
}

void Queen::Backtrack(int t)
{
if (t>n)
{
sum++;
}
else
{
for (int i=t; i<=n; i++)
{
swap(&x[i],&x[t]);
if (Place(t))
Backtrack(t+1);
swap(&x[i],&x[t]);
}
}
}

int nQueen(int n)
{
Queen X;
X.n=n;
X.sum=0;
int *p=new int[n+1];
for (int i=0;i<=n; i++)
p[i]=i;
X.x=p;
X.Backtrack(1);
delete[] p;
return X.sum;
}

int main()
{
int x,y;
x=4;
y=nQueen(x);
printf(“sum=%d\n”,y);
return 0;
}
要求:
1.分析以上两段程序;
2.在程序中添加一些代码,使其能够输出所有的解。
3.比较两段程序的时间效率, N皇后问题中N的值比较大时,记录两段程序的运行时间。

五、实验题

  1. 教材159页 算法实现题 5-8整数变换问题。整数i的两种变换定义为,(向下取整);设计一个算法求给定两个整数a和b,用最少次数的和变换将整数a变换为b;例如
    实现提示:
    观察f和g两个操作可知,f总是使得i变大,g总是使得i变小。因此在决定让x执行哪个操作之前可以先判断i和目标值m之间的大小关系。如果x>m,就让其执行g操作;反之,执行f操作。
    问题的解分为两种情况,一种是有解,即n可以通过函数变换成m;另一种是无解,即n无法通过函数变换成m。
    有解的情况比较容易,只需要判断最后的i是否等于m即可。如果i等于m,那么说明n已经被变换成m了,递归返回。
    无解的情况可用下例分析。假设我们的输入n=9,m=5。
    n>m,执行g,n=[9/2]=4
    n<m,执行f,n=3*4=12
    n>m,执行g,n=[12/2]=6
    n>m,执行f,n=[6/2]=3

n<m,执行g,n=3*3=9
n>m,执行f,n=[9/2]=4
如果n的值陷入了一个重复的循环,如果在递归的过程中,出现了前面计算过的元素,那就说明n是无法转换成m的。这种方法实现稍微复杂,需要判断当前所求出的数值之前是否出现过。 另一种简单的处理方式: 对于m无论如何变换都不能变为n的情况,可以加一个判断条件,比如深度达一个较大值为止(如1000)。
回溯法, 用子集树实现,子集树结构为:

回溯返回条件有两个,一个是i等于m,另一个是出现了重复的数字。第二个返回条件可以用一个函数test来判断。
剪枝条件:
显示约束:如果x>m,就剪掉它的左子树;如果x<m,就剪掉它的右子树;
隐式约束:如果在某次计算的过程中发现当前的计算次数已经大于或等于最少计算次数了,那么就剪掉这个分支。

  1. 教材156页 算法实现题 5-1子集和数问题。

  2. 教材161页 算法实现题 5-13 工作分配问题。(选做)
    实现提示:该问题的解空间是一棵排列树,可用搜索排列树的回溯框架实现。
    用一个二维数组C[][]存放费用,其中C[i][j] 表示i号工人完成j号工作所需的费用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值