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

22 篇文章 0 订阅

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

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

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

打个比方,递归好比是一个军队要通过一个迷宫,到了第一个分岔口,有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[N]={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[N]++; )<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[N]={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;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值