您的位置:首页 > 其它

回溯法的分析及N皇后问题

2015-03-27 23:31 169 查看
所谓的回溯法实质就是深度优先搜索,在每个路径上一直向前寻找,到达某个结点不满足约束函数时,就回溯到其父结点,从父结点的其他子树向下寻找。

当某条路径可以一直伸展到叶子结点时,就在叶子结点上继续遍历,遍历的结果必然是叶子结点的某种情况不满足约束函数,这时就要回溯到叶子结点的父结点,从父结点的其他子树向下寻找,这个子树同样也要遍历叶子结点的所有情况。

回溯实际上是递归的展开,但实际上两者的指导思想并不一致。

打个比方,递归好比是一个军队要通过一个迷宫,到了第一个分岔口,有3条路,将军命令3个小队分别去探哪条路能到出口,3个小队沿着3条路分别前进,各自到达了路上的下一个分岔口,于是小队长再分派人手各自去探路——只要人手足够(对照而言,就是计算机的堆栈足够),最后必将有人找到出口,从这人开始只要层层上报直属领导,最后,将军将得到一条通路。所不同的是,计算机的递归法是把这个并行过程串行化了。

而回溯法则是一个人走迷宫的思维模拟——如果他没有办法在分岔口留下标记,他只能寄希望于自己的记忆力。

1.递归回溯法

一定要注意:void型递归函数的返回是隐形的,即当前层条件不满足,就会自动返回到上一层。

下面的Queen代码中,如果当前层没有找到合适的位置放置皇后,即循环体中的部分就不会执行,自然也不会执行循环体中的递归函数 Queen(row+1); ,说明上一行选择的列不合适,递归返回到上一层,执行Queen_array[row]=0; 然后col++。重新查找合适的列,如果本层递归仍然查找不到,就继续向上一层回溯,执行Queen_array[row]=0; 然后col++ ……

#include<stdio.h>
#include<math.h>

#define N 8

int count=0;
int Queen_array
={0};

int Pos_Feasible(int row,int col)
{
int i;
for(i=0;i<row;i++){
//col不能用Queen_array[row]代替,因为此时Queen_array[row]=0,还未被赋值为col
if(col==Queen_array[i]||(abs(col-Queen_array[i])==row-i))
return 0;
}
return 1;
}

void print_result( )
{
int i;
for(i=0;i<N;i++){
printf("%d ",Queen_array[i]+1);
}
printf("\n");
}

/*当递归回溯到上一层,再从上一层递归到这一层时,这一层的所有临时数据被销毁,重新赋值*/
void Queen(int row)
{
int col;
for(col=0;col<N;col++){
if(Pos_Feasible(row,col)){        //在这一行时Queen_array[row]=0,还未被赋值为col
Queen_array[row]=col;
if(row==N-1){
print_result( );
count++;
}
Queen(row+1);
}
}            //如果这一层没找到合适的
}

int main()
{
Queen(0);
printf("total = %d\n", count);
return 0;
}


2.回溯法

由递归回溯法的分析,我们可以知道,如果一直向上一层回溯,使得回溯到最上层(第一行),col 也增加到了最后一列,但仍不合适,程序仍要向上层回溯,可是没有上层函数了,此时就退出了程序,我们可以利用这一点(把行号作为循环条件,如果行号小于1了则退出循环——此时就是回溯到了最上层仍找不到合适的列还想再向上找的情况)作为循环的条件而不需要递归。

程序如下:

#include<stdio.h>
#include<math.h>
int x[100];

int count=0;

bool place(int k)//考察皇后k放置在x[k]列是否发生冲突
{
int i;
for(i=1;i<k;i++)
if(x[k]==x[i]||abs(k-i)==abs(x[k]-x[i]))
return false;
return true;
}

void queue(int n)
{
int i,k;
for(i=1;i<=n;i++)
x[i]=0;
k=1;
while(k>=1)
{
x[k]++;   //在下一列放置第k个皇后
while(x[k]<=n&&!place(k))
x[k]=x[k]+1;//搜索下一列
if(x[k]<=n&&k==n)//得到一个输出
{
for(i=1;i<=n;i++)
printf("%d ",x[i]);
printf("\n");
count++;
//return;//若return,就无法在叶子结点上继续遍历( x
++; )<span style="font-family: Arial, Helvetica, sans-serif;">,就找不到可以让它回溯的点,也就无法回溯,则只求出其中一种解</span>
}
else if(x[k]<=n&&k<n)
k++;//放置下一个皇后(下一行)
else
{
x[k]=0;//重置x[k],回溯。必须要重置,因为当回溯到上一层找到合适列(路径)时,还要伸展到这一层从头(第0列,col=0)开始寻找
k--;
}
}
printf("%d\n",count);
}

int main()
{
int n;
printf("输入皇后个数n:\n");
scanf("%d",&n);
queue(n);
return 0;
}


3.精简版 ( 由1演化而来 )

#include<stdio.h>
#include<math.h>

#define N 8

int count=0;
int Queen_array
={0};

void print_result( )
{
int i;
for(i=0;i<N;i++){
printf("%d ",Queen_array[i]+1);
}
printf("\n");
}

void Queen(int row)
{
if(row==N){   //注意是N,不是 N-1
count++; print_result( );  //执行完这一步就返回上一层递归继续col++
}
else for(int col=0;col<N;col++){
int ok=1;
Queen_array[row]=col;
for(int i=0;i<row;i++){
if(col==Queen_array[i]||abs(col-Queen_array[i])==row-i){
ok=0;break;
}
}
if(ok) Queen(row+1);
}
}

int main()
{
Queen(0);
printf("total = %d\n", count);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: