c和c++的一些训练题(3)(8皇后游戏)

八皇后游戏的说明:
问题的提出:八皇后是个古老而有趣的游戏,是由高斯于1850年首先提出的。要求在国际象棋的棋盘上放置八个皇后,使其不能相互攻击,即任意两个皇后不能处于棋盘的同一行、同一列和同一条对角线上。试问有多少种放法?

基本思想是:先把皇后放在(0,0)位置,然后把1号皇后放在(1,j)位置,使其满足要求。接着放2号皇后,依此类推。遇到某个皇后如把她无论放在该行的任意位置均不满足要求,则前一个皇后放置不当,须重新放置前一皇后,如8个皇后均按要求放置好,这就是一次成功的摆法。

方法1:

我将采用递归的思想来完成八皇后问题。 递归算法是函数通过不断对自己的调用来求得最终结果的一种思维巧妙但开销很大的算法(总结来自百度知道)。一般不推荐使用。

思路:假设棋盘的前7行都满足条件,到了第8行,判断是否满足条件,满足则输出棋盘。如果没到第8行,则在没行的列数中进行循环,直到找到合适的位置为止。

代码为:

// test3.cpp : 定义控制台应用程序的入口点。
/*recursion*/
#include "StdAfx.h"
#include <iostream>

using namespace std;
int count=0;
int notdanger(int row, int j, int (*chess)[8])
{
	int i,k;
	int flag1=0, flag2=0, flag3=0, flag4=0, flag5=0, flag6=0;
	for (i=0; i<8; i++)
	{
		if(*(*(chess+i)+j)!=0)
		{
			flag1=1;
			break;
		}
	}
	for ( i=row, k=j; i>=0 && k>=0; i--, k--)
	{
		if (*(*(chess+i)+k)!=0)
		{
			flag3=1;
			break;
		}
	}
	//right down
	for (i=row, k=j; i<8 && k<8; i++, k++)
	{
		if (*(*(chess+i)+k)!=0)
		{
			flag4=1;
			break;
		}
	}
	//right up
	for (i=row, k=j; i>=0 && k<8; i--, k++)
	{
		if (*(*(chess+i)+k)!=0)
		{
			flag5=1;
			break;
		}
	}
	//left down
	for (i=row, k=j; i<8 && k>=0; i++, k--)
	{
		if (*(*(chess+i)+k)!=0)
		{
			flag6=1;
			break;
		}
	}
	if (flag1 || flag2 || flag3 || flag4 || flag5 || flag6 )
	{
		return 0;
	}
	else
	{
		return 1;
	}
}
void EightQueen(int row, int col, int (* chess)[8] )
{
	int chess2[8][8];
	for (int i=0; i<8; i++)
	{
		for (int j=0; j<8; j++)
		{
			chess2[i][j]=chess[i][j];
		}
	}
	if (row==8)
	{
		count=count+1;
		cout<<"第"<<count<<"种方法:"<<endl;
		for (int i=0; i<8; i++)
		{
			for (int j=0; j<8; j++)
			{
				cout<<chess2[i][j];
			}
			cout<<endl;
		}
	} 
	else
	{
		for (int j=0; j<col;j++)
		{
			//notDanger
			if (notdanger(row,j,chess2))
			{
				for (int i=0; i<8; i++)
				{
					*(*(chess2+row)+i)=0;
				}
				*(*(chess2+row)+j)=1;
				EightQueen(row+1,col,chess2);
			}
		}
	}
}
int main()
{
	int chess[8][8];
	//Init
	for (int i=0; i<8; i++)
	{
		for (int j=0; j<8; j++)
		{
			chess[i][j]=0;
		}
	}
	EightQueen(0, 8, chess);
	cout<<"一共有"<<count<<"种摆放方式。"<<endl;
	system("pause");
	return 0;
}


皇后是一个古老而著名的问,是回溯算法的典型例。该问是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。   高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。   对于八皇后的实现,如果结合动态的图形演示,则可以使算法的描述更形象、更生动,使教学能产生良好的效果。下面是用Turbo C实现的八皇后的图形程序,能够演示全部的92组解。八皇后动态图形的实现,主要应解决以下两个问。   (1)回溯算法的实现   (a)为解决这个问,我们把棋盘的横坐标定为i,纵坐标定为j,i和j的取值范围是从1到8。当某个皇后占了位置(i,j)时,在这个位置的垂直方向、水平方向和斜线方向都不能再放其它皇后了。用语句实现,可定义如下三个整型数组:a[8],b[15],c[24]。其中:   a[j-1]=1 第j列上无皇后   a[j-1]=0 第j列上有皇后   b[i+j-2]=1 (i,j)的对角线(左上至右下)无皇后   b[i+j-2]=0 (i,j)的对角线(左上至右下)有皇后   c[i-j+7]=1 (i,j)的对角线(右上至左下)无皇后   c[i-j+7]=0 (i,j)的对角线(右上至左下)有皇后   (b)为第i个皇后选择位置的算法如下:   for(j=1;j<=8;j++) /*第i个皇后在第j行*/   if ((i,j)位置为空)) /*即相应的三个数组的对应元素值为1*/   {占用位置(i,j) /*置相应的三个数组对应的元素值为0*/   if i<8   为i+1个皇后选择合适的位置;   else 输出一个解   }   (2)图形存取   在Turbo C语言中,图形的存取可用如下标准函数实现:   size=imagesize(x1,y1,x2,y2) ;返回存储区域所需字节数。   arrow=malloc(size);建立指定大小的动态区域位图,并设定一指针arrow。   getimage(x1,y1,x2,y2,arrow);将指定区域位图存于一缓冲区。   putimage(x,y,arrow,copy)将位图置于屏幕上以(x,y)左上角的区域。   (3)程序清单如下   #include <graphics.h>   #include <stdlib.h>   #include <stdio.h>   #include <dos.h>   char n[3]={'0','0'};/*用于记录第几组解*/   int a[8],b[15],c[24],i;   int h[8]={127,177,227,277,327,377,427,477};/*每个皇后的行坐标*/   int l[8]={252,217,182,147,112,77,42,7}; /*每个皇后的列坐标*/   void *arrow;   void try(int i)   {int j;   for (j=1;j<=8;j++)   if (a[j-1]+b[i+j-2]+c[i-j+7]==3) /*如果第i列第j行为空*/   {a[j-1]=0;b[i+j-2]=0;c[i-j+7]=0;/*占用第i列第j行*/   putimage(h[i-1],l[j-1],arrow,COPY_PUT);/*显示皇后图形*/   delay(500);/*延时*/   if(i<8) try(i+1);   else /*输出一组解*/   {n[1]++;if (n[1]>'9') {n[0]++;n[1]='0';}   bar(260,300,390,340);/*显示第n组解*/   outtextxy(275,300,n);   delay(3000);   }   a[j-1]=1;b[i+j-2]=1;c[i-j+7]=1;   putimage(h[i-1],l[j-1],arrow,XOR_PUT);/*消去皇后,继续寻找下一组解*/   delay(500);   }}   int main(void)   {int gdrive=DETECT,gmode,errorcode;   unsigned int size;   initgraph(&gdrive,&gmode,"");   errorcode=graphresult();   if (errorcode!=grOk)   {printf("Graphics error\n");exit(1);}   rectangle(50,5,100,40);   rectangle(60,25,90,33);   /* 画皇冠 */   line(60,28,90,28);line(60,25,55,15);   line(55,15,68,25);line(68,25,68,10);   line(68,10,75,25);line(75,25,82,10);   line(82,10,82,25);line(82,25,95,15);   line(95,15,90,25);   size=imagesize(52,7,98,38); arrow=malloc(size);   getimage(52,7,98,38,arrow); /* 把皇冠保存到缓冲区 */   clearviewport();   settextstyle(TRIPLEX_FONT, HORIZ_DIR, 4);   setusercharsize(3, 1, 1, 1);   setfillstyle(1,4);   for (i=0;i<=7;i++) a=1;   for (i=0;i<=14;i++) b=1;   for (i=0;i<=23;i++) c=1;   for (i=0;i<=8;i++) line(125,i*35+5,525,i*35+5); /* 画棋盘 */   for (i=0;i<=8;i++) line(125+i*50,5,125+i*50,285);   try(1); /* 调用递归函数 */   delay(3000);   closegraph();   free(arrow);   }   二、循环实现 Java   /*   * 8皇后:   *   * 问描述:   * 在一个8×8的棋盘里放置8个皇后,要求每个皇后两两之间不相冲突   *(在每一横列,竖列,斜列只有一个皇后)。   *   * 数据表示:   * 用一个 8 位的 8 进制数表示棋盘上皇后的位置:   * 比如:45615353 表示:   * 第0列皇后在第4个位置   * 第1列皇后在第5个位置   * 第2列皇后在第6个位置   * 。。。   * 第7列皇后在第3个位置   *   * 循环变量从 00000000 加到 77777777 (8进制数)的过程,就遍历了皇后所有的情况   * 程序中用八进制数用一个一维数组 data[] 表示   *   * 检测冲突:   * 横列冲突:data == data[j]   * 斜列冲突:(data+i) == (data[j]+j) 或者 (data-i) == (data[j]-j)   *   * 好处:   * 采用循环,而不是递规,系统资源占有少   * 可计算 n 皇后   * 把问线性化处理,可以把问分块,在分布式环境下用多台计算机一起算。   *   * ToDo:   * 枚举部分还可以进行优化,多加些判断条件速度可以更快。   * 输出部分可以修改成棋盘形式的输出   *   * @author cinc 2002-09-11   *   */   public class Queen {   int size;   int resultCount;   public void compute ( int size ) {   this.size = size;   resultCount = 0;   int data[] = new int[size];   int count; // 所有可能的情况个数   int i,j;   // 计算所有可能的情况的个数   count = 1;   for ( i=0 ; i<size ; i++ ) {   count = count * size;   }   // 对每一个可能的情况   for ( i=0 ; i<count ; i++ ) {   // 计算这种情况下的棋盘上皇后的摆放位置,用 8 进制数表示   // 此处可优化   int temp = i;   for ( j=0 ; j<size ; j++ ) {   data [j] = temp % size;   temp = temp / size;   }   // 测试这种情况是否可行,如果可以,输出   if ( test(data) )   output( data );   }   }   /*   * 测试这种情况皇后的排列是否可行   *   */   public boolean test( int[] data ) {   int i,j;   for ( i=0 ; i<size ; i++ ) {   for ( j=i+1 ; j<size ; j++ ) {   // 测试是否在同一排   if ( data == data[j] )   return false;   // 测试是否在一斜线   if ( (data+i) == (data[j]+j) )   return false;   // 测试是否在一反斜线   if ( (data-i) == (data[j]-j) )   return false;   }   }   return true;   }   /*   * 输出某种情况下皇后的坐标   *   */   public void output ( int[] data ) {   int i;   System.out.print ( ++resultCount + ": " );   for ( i=0 ; i<size ; i++ ) {   System.out.print ( "(" + i + "," + data + ") " );   }   System.out.println ();   }   public static void main(String args[]) {   (new Queen()).compute( 8 );   }   }   三、八皇后的Qbasic版的解决方案   10 I = 1   20 A(I) = 1   30 G = 1   40 FOR K = I - 1 TO 1 STEP -1   50 IF A(I) = A(K) THEN 70   60 IF ABS(A(I) - A(K)) <> I - K THEN 90   70 G = 0   80 GOTO 100   90 NEXT K   100 IF I <> 8 THEN 180   110 IF G = 0 THEN 180   120 FOR L = 1 TO 8   130 PRINT USING “##”; A(L);   140 NEXT L   150 PRINT “*”;   160 M = M + 1   170 IF M MOD 3 = 0 THEN PRINT   180 IF G = 0 THEN 230   190 IF I = 8 THEN 230   200 I = I + 1   210 A(I) = 1   220 GOTO 30   230 IF A(I) < 8 THEN 270   240 I = I - 1   250 IF I = 0 THEN 290   260 GOTO 230   270 A(I) = A(I) + 1   280 GOTO 30   290 PRINT   300 PRINT “SUM=”; USING “##”; M;   310 PRINT   320 END   四、八皇后的高效解法-递归版   //8 Queen 递归算法   //如果有一个Q 为 chess=j;   //则不安全的地方是 k行 j位置,j+k-i位置,j-k+i位置   class Queen8{   static final int QueenMax = 8;   static int oktimes = 0;   static int chess[] = new int[QueenMax];//每一个Queen的放置位置   public static void main(String args[]){   for (int i=0;i<QueenMax;i++)chess=-1;   placequeen(0);   System.out.println("\n\n\n八皇后共有"+oktimes+"个解法 made by yifi 2003");   }   public static void placequeen(int num){ //num 为现在要放置的行数   int i=0;   boolean qsave[] = new boolean[QueenMax];   for(;i<QueenMax;i++) qsave=true;   //下面先把安全位数组完成   i=0;//i 是现在要检查的数组值   while (i<num){   qsave[chess]=false;   int k=num-i;   if ( (chess+k >= 0) && (chess+k < QueenMax) ) qsave[chess+k]=false;   if ( (chess-k >= 0) && (chess-k < QueenMax) ) qsave[chess-k]=false;   i++;   }   //下面历遍安全位   for(i=0;i<QueenMax;i++){   if (qsave==false)continue;   if (num<QueenMax-1){   chess[num]=i;   placequeen(num+1);   }   else{ //num is last one   chess[num]=i;   oktimes++;   System.out.println("这是第"+oktimes+"个解法 如下:");   System.out.println("第n行: 1 2 3 4 5 6 7 8");   for (i=0;i<QueenMax;i++){   String row="第"+(i+1)+"行: ";   if (chess==0);   else   for(int j=0;j<chess;j++) row+="--";   row+="++";   int j = chess;   while(j<QueenMax-1){row+="--";j++;}   System.out.println(row);   }   }   }   //历遍完成就停止   }   }
二、 算法思想: 采用回溯法解决八皇后。从第一行开始,放第一个皇后,放好皇后以后,她所在的行,列和对角线上的每一个位置就是她的管辖范围,别的皇后没有权利干涉,否则死无藏身之地。 然后,第二个皇后,从第二行的第一列开始判断所在的位置是否是别的皇后的管辖范围,找到第一个还没有被占据的位置,则将其占为己有。暂时,该皇后停在该位置。然后,第三个到第八个皇后依次从第三行,第四行,… ,到第八行的第一列开始寻求自己的位置。假如到第i个皇后时,已经没有任何位置可选,则第i-1个皇后必须往后移动进行协调,同样,假如第i-1个皇后往后移动时没有找到空位置,则第i-2个皇后必须往后移动,进行协调,当找到空位置时,暂时停下,将下一个皇后重新从第一列开始寻找空位置。重复上述过程,直到所有皇后都停下来。则得到了第一个解。要想产生所有的解,则当产生第一个解以后,第八个皇后往后移动,找下一个可以利用的空位置,找不到,则第七个皇后必须往后移动,若找到空位置则停下,第八个皇后从第八行第一列重新试探,找到空位置。一直这样,直到第一个皇后将第一行遍历完。得到的解就是所有解。 三、 概要设计: ***************类型及相关变量定义***************** //位置信息类型 typedef struct { int row; int col; }PosType; //皇后类型 typedef struct Queen{ PosType pos; int number; //第几号皇后 }QueenType; //栈节点类型 typedef struct Note{ QueenType queen; struct Note *next; }NoteType; //棋盘,某一位置chessboard[i][j]上有皇后,则该位的值变为皇后序号。同样,该皇后的势 //力范围内的位置上的值全部变为该皇后的序号。 int chessboard[8][8]; //结果集,共92种解,每一种解中记录8个位置信息。 PosType ResultSet[92][8]; //定义一个栈,保存信息 Typedef struct{ NoteType head; Int size; }QueenStack; //定义一个栈,存放皇后信息 QueenStack qstack; *************相关操作**************** //初始化棋盘,开始时每个位置上都没有皇后,值全为0;并给8个皇后编号。 void initChessboard(); //回溯求八皇后的所有解,皇后协调算法 void queenCoordinate(); //输出所有解 void printResult();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值