zoukankan      html  css  js  c++  java
  • 八皇后之回溯法解决

    问题描述:

    要在8*8的国际象棋棋盘中放8个皇后,使任意两个皇后都不能互相吃掉。规则是皇后能吃掉同一行、同一列、同一对角线的棋子。如下图即是两种方案:



    解决方案:

    8*8的棋盘要摆放8个皇后,且不能同行同列同对角线,那么每行必定会有一个皇后。我们可以设一个数组a用来存放每一行皇后的位置,元素值表示第几列(如a[1]=5表示第一行的皇后处于第五个格)。然后只需要求出数组a的值 问题就解决了,下面介绍三种回溯解法:


    1、八个for循环。用枚举的办法,八个for循环分别枚举每一行的8个位置,但是我们不用全部枚举完,可以采用“剪枝策略”,即遇到不适合的情况就回溯。例如当a[1]=4,第二行a[2]=4与a[1]同列,不符合题意。接下来的六个循环就不用穷举下去了,直接"continue;"去检验a[2]=5.....具体代码如下:

    void main()
    {
    	int a[9];
    	int i,t=1;
    	for(a[1]=1;a[1]<9;a[1]++)
    		for(a[2]=1;a[2]<9;a[2]++)
    		{
    			if(!Check(a,2))	continue;
    			for(a[3]=1;a[3]<9;a[3]++)
    			{
    				if(!Check(a,3))	continue;
    				for(a[4]=1;a[4]<9;a[4]++)
    				{
    					if(!Check(a,4))	continue;
    					for(a[5]=1;a[5]<9;a[5]++)
    					{
    						if(!Check(a,5))	continue;
    						for(a[6]=1;a[6]<9;a[6]++)
    						{
    							if(!Check(a,6))	continue;
    							for(a[7]=1;a[7]<9;a[7]++)
    							{
    								if(!Check(a,7))	continue;
    								for(a[8]=1;a[8]<9;a[8]++)
    								{
    									if(!Check(a,8))	continue;
    									else 
    									{
    										printf("第%d种解法:
    ",t++);
    										for(i=1;i<9;i++)
    											printf("第%d个皇后:%d
    ",i,a[i]);
    										printf("
    
    ");
    }		}	}	}	}	}	}	}	}

    /////////////////////////////////Check函数功能:检验第n行的皇后是否和之前的皇后有冲突,没有的话返回1
    int Check(int a[],int n)
    {
    	for(int i=1;i<n;i++)
    	{
    		if(abs(a[i]-a[n])==abs(i-n) || a[i]==a[n])//////////////见下面注释
    			return 0;
    	}
    	return 1;
    }

    代码注释:

    某一行的皇后a[n]不能和之前的皇后a[i]位置有冲突,约束条件为:

    a、不在同一列:a[n] != a[i]

    b、不在同一行:因为现在是每一行求一个皇后的位置,所以同一行不会有冲突,不需要考虑。

    c、不在同一对左角线:a[n]-a[i] != n-i

    d、不在同一右对角线:a[n]-a[i] != -(n-i)

    条件c和d可以合成:abs(a[n]-a[i]) != abs(n-i)

    总结:其实这里用到的就是深度优先搜索的思想,从第一行的皇后一直深入去找第二行、第三行...皇后的位置。其中加上了约束条件Check函数进行“剪枝”。这就是回溯算法的思想:深度优先搜索,遇到不满足的情况就进行回溯。



    2、方法一的优化。上述代码易读、易懂,但是用八个for循环不免显得很累赘,而且如果要求在100*100的棋盘上放100个皇后这种“N皇后问题“呢?难道用100个for循环吗?我们来把代码优化一下,用到的思想还是和方法一相同:深度优先搜索、回溯。具体代码如下:

    void main()
    {
    	int a[256]={0};
    	int i=1,j,n,t=1;////////////////////////////////////i表示当前正在搜索第i行的皇后位置
    	printf("请输入几皇后?n=");
    	scanf("%d",&n);
    	while(i>0)
    	{
    		for(a[i]++;a[i]<=n;a[i]++)
    		{
    			if(Check(a,i))//////////////////////////////如果第i行的皇后与之前的皇后位置上没有冲突,则break跳出循环
    				break;
    		}
    		if(a[i]<=n)/////////////////////////////////////如果a[i]<=n,即上面的for循环是由“break;”跳出来的,即第i行皇后的位置符合条件
    		{
    			if(i==n)////////////////////////////////////找到一组解,输出
    			{
    				printf("第%d种解法:
    ",t++);
    				for(j=1;j<=n;j++)
    					printf("第%d个皇后:%d
    ",j,a[j]);
    				printf("
    
    ");
    			}
    			else////////////////////////////////////////未找完
    			{
    				i++;
    				a[i]=0;
    			}
    		}
    		else
    			i--;////////////////////////////////////////回溯
    	}
    }
    代码注释:上面用到的Check函数和方法一的Check函数相同。

    总结:虽然上面代码中只用到两层循环,但是思想、思路和方法一都是一样的,时间复杂度也是和方法一的时间复杂度相同。当n大于10之后运算就已经比较困难了。



    3、递归实现。上面两种方法都是用到了深度优先搜索,而一般而言,深度优先搜索都是可以用递归来实现的。下面我们用递归的方式解决八皇后问题。具体代码如下:

    int a[20],n,i,j,t=1;////////////////////////////////////////全局变量
    
    void Try(int i)
    {
    	int j,k;
    	for(j=1;j<=n;j++)
    	{
    		a[i]=j;
    		if(Check(a,i))///////////////////////////////////////如果第j列不会与之前的皇后冲突
    		{
    			if(i<n)//////////////////////////////////////////如果i<n,即还没有找到八个皇后,继续递归
    				Try(i+1);
    			else ////////////////////////////////////////////如果找到了一组解就输出
    			{
    				printf("第%d种解法:
    ",t++);
    				for(k=1;k<=n;k++)
    					printf("第%d个皇后:%d
    ",j,a[k]);
    				printf("
    
    ");
    }	}	}	}
    
    void main()
    {
    	printf("几皇后?n=");
    	scanf("%d",&n);
    	Try(1);
    }

     代码注释:

    a、此处递归的思路很简单,每一层递归表示一行皇后,j表示列,即a[i]=j表示第i行的皇后位置在第j列。

    b、以上用到的Check函数与方法一的Check函数相同。

    我不清楚是什么原因,递归的速度竟然明显比前面的两种方法快??

  • 相关阅读:
    HDU 1495 非常可乐
    ja
    Codeforces Good Bye 2016 E. New Year and Old Subsequence
    The 2019 Asia Nanchang First Round Online Programming Contest
    Educational Codeforces Round 72 (Rated for Div. 2)
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises)
    AtCoder Regular Contest 102
    AtCoder Regular Contest 103
    POJ1741 Tree(点分治)
    洛谷P2634 [国家集训队]聪聪可可(点分治)
  • 原文地址:https://www.cnblogs.com/Bone-ACE/p/4531307.html
Copyright © 2011-2022 走看看