從八皇后問題剖析回溯算法

首先讓我們來看一下什麼是八皇后問題
在8*8的棋盤上放置8個皇后而彼此不受攻擊(即在棋盤的任一行,任一列和任一對角線上不能放置2個皇后)
在這裏插入圖片描述

這道題運用回溯算法,那麼什麼是回溯算法?

簡單來說,就是先按照一種方法向前試探,當發現這種方法不是最優解或者不能到達目標時,退回一步,換下一種方法繼續試探,知道找到終點爲止

其實回溯算法都有一個很相似的套路

void go(int k)
{
    for(i=1;i<=k;i++)
    {
  	if(符合條件)
  	{
  		 ……
   		if(到達終點) {}
   		else go(k+1);
   		//進行回溯
   		返回一步 …… 
  	} 
    }
} 

那麼這個八皇后問題也是同理
比如
在這裏插入圖片描述先在(1,1)放置第一個皇后
然後進入第二行放置第二個皇后
先進入(2,1),發現與第一個衝突,右移,到達(2,2)發現還與第一個衝突,繼續右移,到達(2,3),沒有衝突,所以第二個皇后成功佔領
在這裏插入圖片描述

第三個皇后佔領的過程同理,先到達(3,1),發現與(1,1)衝突,右移,到達(3,2)發現與(2,3)衝突,右移,到達(3,3)與(2,3)衝突,右移,到達(3,4),與(2,3)衝突,右移,到達(3,5),沒有衝突,成功佔領

在這裏插入圖片描述
那麼如何體現回溯呢?
就像下圖
在這裏插入圖片描述

我們此時已經填充完前7個皇后了,但是發現第8個皇后無論在哪都會與其他皇后衝突,這時我們就倒退一步,將第7個皇后換一下位置,繼續右移,放在(7,7)與第四個皇后衝突,(7,8)與第二個皇后衝突,此時第7個皇后已經沒有位置可以選擇,那就繼續回溯,移動第6個皇后,將他右移,不斷尋找合適位置

這就是回溯
那麼具體如何處理這個八皇后問題呢

首先我們應該知道,爲了避免衝突就應該沒有同一行,同一列,對角線上沒有兩個皇后

在這裏插入圖片描述由圖中易得,在同一右下方對角巷上的元素,行數-列數總是相同

同理可知,左下方向的對角線元素,行數+列數總是相同

之後就可以大致得出關鍵代碼

準備數組queen,表示在第i行的第j列放置元素queen[i]=j;
flag數組表示同一列有無衝突
d1表示右下方向對角線是否衝突,這裏+7是爲了讓所有的負數都變爲非負數
if(n<8)就表示8個皇后沒有排滿,還需要繼續遞歸
否則就打印結果

下面的回溯只有打印結果步驟結束之後在可以運行,此時,需要撤銷最後一步,尋求其他結果

//判斷位置是否衝突 
if((!flag[col])&&(!d1[n-col+7])&&(!d2[n+col]))
  {
   queen[n]=col;//在第n行放置皇后 
   flag[col]=1;//佔領col列 
   d1[n-col+7]=1;//佔領兩個對角線 
   d2[n+col]=1;
   if(n<8)
    display(n+1);//8個皇后沒有擺完,就繼續遞歸 
   else
    print();//n=8說明已經排列完成 
   
   //回溯:考慮其他可行方案 
   flag[col]=0;
   d1[n-col+7]=0;
   d2[n+col]=0;  
  }

下面附上全部代碼

#include<iostream>
using namespace std;

int queen[9]={0};//第i個皇后所在列數 
int flag[9]={0,0,0,0,0,0,0,0,0};//表示第i列是否可佔 
int d1[17]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//表示“/”對角線是否可佔 
int d2[17]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//表示“\”對角線是否可佔
int number=0;//種類數 

void print();//打印函數 
void display(int n);//回溯函數 

int main()
{
	display(1);
	return 0;
}
void display(int n)
{
	int col;
	for(col=1;col<=8;col++)//每種皇后都有8種可能 
	{
		if((!flag[col])&&(!d1[n-col+7])&&(!d2[n+col]))//判斷位置是否衝突 
		{
			queen[n]=col;//在第n行放置皇后 
			flag[col]=1;//佔領col列 
			d1[n-col+7]=1;//佔領兩個對角線 
			d2[n+col]=1;
			if(n<8)
				display(n+1);//8個皇后沒有擺完,就繼續遞歸 
			else
				print();//n=8說明已經排列完成 
			
			//回溯:考慮其他可行方案 
			flag[col]=0;
			d1[n-col+7]=0;
			d2[n+col]=0;		
		}
	}
}
void print()
{
	int col,i,j;
	number++;
	cout<<"No."<<number<<endl;
	int table[9][9]={0};
	for(col=1;col<=8;col++)
	{
		table[col][queen[col]]=1;
	}
	for(i=1;i<=8;i++)
	{
		for(j=1;j<=8;j++)
		{
			cout<<table[i][j]<<" ";
		}
		cout<<endl;
	}
}

運行結果如下,可知8皇后有92種可能

在這裏插入圖片描述
由此我們知道,回溯算法是一個類似枚舉的試探性過程,當發現已不滿足求
解條件時,就“回溯”返回,嘗試別的路徑,能進則進,不能進再退回來

如果這篇文章對你有幫助,記得點贊關注哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章