回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。
步骤介绍
用回溯算法解决问题的一般步骤为:
一、定义一个解空间,它包含问题的解。
二、利用适于搜索的方法组织解空间。
三、利用深度优先法搜索解空间。
四、利用限界函数避免移动到不可能产生解的子空间。
问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题.
经典的N后问题
基本思路:
用n元组x[1:n]表示n后问题的解。其中,x[i]表示皇后i放置在棋盘的第i行的第x[i]列。由于不容许将2个皇后放在同一列上,所以解向量中的 x[i]互不相同。2个皇后不能放在同一斜线上是问题的隐约束。对于一般的n后问题,这一隐约束条件可以化成显约束的形式。如果将n*n 格的棋盘看做二维方阵,其行号从上到下,列号从左到右依次编号为1,2,...n。从棋盘左上角到右下角的主对角线及其平行线(即斜率为-1的各斜线) 上,2个下标值的差(行号-列号)值相等。同理,斜率为+1的每条斜线上,2个下标值的和(行号+列号)值相等。因此,若2个皇后放置的位置分别是 (i,j)和(k,l),且 i-j = k -l 或 i+j = k+l,则说明这2个皇后处于同一斜线上。以上2个方程分别等价于i-k= j-l 和 i-k =l-j。由此可知,只要|i-k|=|l-j|成立,就表明2个皇后位于同一条斜线上。
1、从空棋盘起,逐行放置棋子。
2、每在一个布局中放下一个棋子,即推演到一个新的布局。
3、如果当前行上没有可合法放置棋子的位置,则回溯到上一行,重新布放上一行的棋子。
#include<stdio.h>
#include<math.h>
#include<stdlib.h> //abs函数
/*a[i]表示皇后位于的第i行第a[i]列,
由于不容许将2个皇后放在同一列上,所以解向量中的 x[i]互不相同
若2个皇后放置的位置分别是 (i,j)和(k,l)只要|i-k|=|l-j|成立,就表明2个皇后位于同一条斜线上。
*/
int check(int a[],int n)
{
int i,j;
for(i=2;i<=n;i++)
for(j=1;j<=i-1;j++)
if ((a[i]==a[j]) ||(abs(a[i]-a[j])==abs(i-j)))
return 0;
return (1);
}
void queen1( )
{
int i;
int a[9];
for(a[1]=1;a[1]<=8;a[1]++)
for(a[2]=1;a[2]<=8;a[2]++)
for(a[3]=1;a[3]<=8;a[3]++)
for(a[4]=1;a[4]<=8;a[4]++)
for(a[5]=1;a[5]<=8;a[5]++)
for(a[6]=1;a[6]<=8;a[6]++)
for(a[7]=1;a[7]<=8;a[7]++)
for(a[8]=1;a[8]<=8;a[8]++)
{
if (check(a,8)==0)
continue;
else
{
for(i=1;i<=8;i++)
printf("%d",(a[i]));
printf("\n");
}
}
}
int main()
{
queen1();
}
回溯非递归
<pre name="code" class="cpp">#include<stdio.h>
#include<stdlib.h> //abs函数
int a[9];
int check(int a[],int n)
{
int i;
for(i=1;i<=n-1;i++)
if ((a[i]==a[n]) ||(abs(a[i]-a[n])==abs(i-n)))
return 0;
return (1);
}
void backdate (int n)
{ int i,k;
a[1]=0; k=1;
while( k>0 )
{
a[k]=a[k]+1;
while ((a[k]<=n) &&(check(a,k)==0)) //搜索第k个皇后位置
a[k]=a[k]+1;
if(a[k]<=n)
{
if(k==n )
{
for(i=1;i<=n;i++)
printf("%d",(a[i]));
printf("\n");
}//找到一组解
else
{
k=k+1;//继续为第k+1个皇后找到位置
a[k]=0;
} //注意下一个皇后一定要从头开始搜索
}else
k=k-1; //回溯
}
}
int main()
{
backdate(8);
}
回溯递归
#include <stdio.h>
#include<stdlib.h> //abs函数
static int n,x[1000];
static long sum;
int Place(int k)
{
for(int j=1;j <k; j++)
if((abs(k-j) == abs(x[j]-x[k]))||(x[j]==x[k])) return 0;
return 1;
}
void Backtrak(int t)
{
if(t>n) {sum++;for(int i=1;i<=n;i++)
printf("%d",(x[i]));
printf("\n");
}
else
for(int i=1; i <= n; i++)
{
x[t] =i;
if(Place(t))Backtrak(t+1);
}
}
int main()
{
int nn;
while(scanf("%d",&nn)!=EOF)
{
n=nn;
sum=0;
for(int i=0;i<=n;i++)
x[i]=0;
Backtrak(1);
printf("%d\n",sum);
}
}