1. 程式人生 > >【C/C++】回溯經典演算法之-->八皇后問題

【C/C++】回溯經典演算法之-->八皇后問題

一、八皇后問題

八皇后問題,是一個古老而著名的問題,是回溯演算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。高斯認為有76種方案。1854年在柏林的象棋雜誌上不同的作者發表了40種不同的解,後來有人用圖論的方法解出92種結果。

二、問題分析

整體思路:

建立一個全域性變數的二維陣列chess;

並初始化全為0

一行一行開始放皇后;

若存在相互攻擊,則皇后向右移一列

若不存在攻擊,進行下一行皇后的放置(這裡要用到遞迴)

要點:

(1)國際象棋的格數

國際像是是8*8,因此,如果放置八個皇后,那麼每一行都有一個

(2)問題具體化

利用二維陣列,用1來代表皇后,其他為0

(3)皇后的放置方法

利用迴圈,判斷位置是否可以放置皇后,若可以,則放置(將其置1)

三、程式碼實現

程式碼塊:

#include<stdio.h>
#include<windows.h>
#define N 8 //可以根據N來修改棋盤的格數 
int count = 0;//設定一個計數器 
int chess[N][N] = {0};//用於存放棋盤的二維陣列 

void print()//列印函式 
{	
	int i = 0;
	int j = 0;
	printf("*****************************************\n");
	for(i = 0; i<N ;i++)
	{
		for(j = 0; j<N ; j++)
		{
			printf("%d ",chess[i][j]);
		}
		printf("\n");
	}
	printf("*****************************************\n");
}

//判斷是否會互吃 
//關鍵條件
//返回1 表示存在互吃
//返回0 表示正常 
int check(int i, int j)//i = 7,j = 4 
{	
	if(i == 0)
		return 0;//表示正常
  
	int k = 0;
	for(k = 0; k<i ; k++)
	{
		if(chess[k][j] == 1)//(0,4)(1,4)...
			return 1;
	}
	for( int s = 0,k = j+1; k<N ;k++)
	{	
						//(7,4)(6,5),(5,6),(4,7)
		if(chess[i-s-1][k] == 1)//(0,11),(1,10),(2,9),(3,8),(4,7)
			return 1;
		s++;
	}
	for(k = 0; k<j ;k++)
	{
		if(chess[i-k-1][j-k-1] == 1)//(6,3)(5,2)(4,1)(3,0)
			return 1;	
	}
	for(k = 0; k<N ; k++)
	{
		if(chess[k][j]==1)
			return 1;
	}
	return 0;
}
//判斷棋盤上是否有一行存在沒有皇后的情況 
//返回0 ,表示棋盤正常(每一行都有皇后)
//返回1 ,表示棋盤有錯 
int check_all()
{
	int i = 0;
	int j = 0;
	int flag = 0;
	for(i = 0; i<N ;i++)
	{	
		flag = 0;
		for(j = 0; j<N ; j++)
		{
			if(chess[i][j]==1)
			{
				flag = 1;
				break;
			}
		}
		if(flag == 0)
			return 1;//有錯 
	}
	return 0;
}
//檢查某一行是否存在皇后
//返回0 表示存在
//返回1 表示沒皇后 
int check_line(int line)
{	
	if(line==0)
		return 0;
	int k = 0;
	int s = 0;
	int flag = 1;
	for(s = 0; s<line-1 ; s++)
	{	
		flag = 1;
		for(k = 0; k<N ;k++)
		{
			if(chess[s][k]==1)
				flag = 0;
		}
		if(flag==1)
			return 1;
	}
	return 0;
}
//遞迴的主要演算法 
void queen(int i,int j)
{		
	//符合,置一,進入下一行
	if(check_line(i)==1)//若該行有皇后,返回 
		return ;
	if((i==(N-1)))//若此時是最後一行 
	{		
		if(check(i,j)==0)//當最後一行的皇后可以放下(表示可以成功放置)	
		{
			chess[i][j] = 1;//將該位置1,表示皇后 
			print();//列印 
			count++;//計數器+1 
		}
	}		
	if(check(i,j)==0)//當可以放皇后時 
	{	
		chess[i][j] = 1;//放入 
		//print();
		//Sleep(1000);
		queen(i+1,0);//進行下一行的皇后放置 
	}
 	if(j==N)//如果j等於列數,表明越界,返回 
		return ;
		
	chess[i][j] = 0;//將該位置0 
	//print();
	//Sleep(500);
	//不符合,置零,右諾
		queen(i,j+1);//將該行皇后右移一位 
}
int main(void)
{	
	queen(0,0);
	printf("\ncount = %d\n",count);
	return 0;
}


執行結果: