(回溯算法)求解N皇后問題

“發光並非太陽的專利,你也可以發光。”
你好,我是夢陽辰!期待與你相遇!

【問題描述】1.設計算法求解N皇后問題,要求給出測試用例,並給出你的程序運行該測試案例之後得到的結果。
N皇后問題研究的是如何將 N個皇后放置在 N×N 的棋盤上,並且使皇后彼此之間不能相互攻擊。

在這裏插入圖片描述
(1) 給定一個整數N,返回所有不同的N皇后問題的解決方案。

(2) 如果只讓求解N皇后問題不同解法的數目,又該如何設計算法。

(1)算法的描述:
1.定義解空間:
n皇后問題要求每一個皇后在不同行、不同列、不同斜線,因此我們可以使用不同行、不同列作爲顯約束,隱約束選擇不同斜線。這樣選擇可以將解空間大大縮小。從根到葉子的路徑就是一個解空間樹。

(2)搜索解空間
1.約束條件:在第i行第j列放置第i個皇后時不能 `前i-1個皇后在同一斜線。根據行列與斜線的映射關係可知,該位置所在斜線的標號,若標記數組中該位置爲false表示此斜線還可以放置皇后,否則該位置不能放。

2.限界條件:該問題不存在放置方案好壞的問題,所以不需要設置限界條件。

3.搜索過程:搜索過程從根開始,以深度優先方式進行搜索,根結點是活結點,並且是當前的擴展結點。在搜索過程中,當前的擴展結點沿縱深方向移向一個新結點, 判斷該新結點是否滿足隱約束。如果滿足,則新結點成爲活結點,繼續深一層的搜索;如果不滿足,則換到該新結點的兄弟結點繼續搜索;如果新結點沒有兄弟結點。或者其他兄弟結點已經全部搜索完畢,則擴展結點成爲死結點,搜索回溯到其父結點處繼續進行。搜索過程直到找到問題的根結點變爲死結點爲止。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<unsigned>nd;
int n, num;
bool p;//選擇是否輸出所有排列方式
vector<bool>l_r, r_l;//標記列,從左到右的斜線,和從右到左的斜線是否已經訪問過
//不能放置物品爲false 否則爲true
inline void init()
{
	nd.resize(n);
	l_r.assign(2 * n - 1, true);
	r_l.assign(2 * n - 1, true);
	for (int i = 0; i < n; i++)nd[i] = i;
	num = 0;
}
inline void mapping(int cur, int& _l_r, int &_r_l)
{
	_l_r = cur + nd[cur];
	_r_l = (n - 1 - cur) + nd[cur];
}
inline bool judge(int cur)//判斷當前位置能否放置皇后
{
	int lr, rl;
	mapping(cur, lr, rl);
	return  l_r[lr] && r_l[rl];
}
void dfs(int cur,int k)//排列樹優化
{
	int i, lr, rl;
	if (cur == n)//找到一種放置方法
	{
		num++;
		if (p) {
			cout << "---------------------------------------------------" << endl;
			cout << "第" << num << "個滿足要求的皇后位置:" << endl;
			i = 0;
			for (auto it : nd)//打印符合要求的位置
			{
				cout << "(" << i++ << "," << it << ") ";
			}
			cout << "\n--------------------------------------------------" << endl;
 
			cout << "---------------------------------------------------" << endl;
			cout << "第" << num+1 << "個滿足要求的皇后位置:" << endl;
			i = 0;
			for (auto it : nd)//打印符合要求的位置
			{
				cout << "(" << i++ << "," << n-1-it << ") ";
			}
			cout << "\n--------------------------------------------------" << endl;
		}
		num++;
 
	}
	else
	{
		for (i = cur; i <k; i++)
		{
			swap(nd[cur], nd[i]);//依次和後面的第cur-k個元素交換位置
			if (judge(cur))//判斷隱約束是否滿足要求
			{
				mapping(cur, lr, rl);//得到映射關係
				//標記
				l_r[lr] = false;
				r_l[rl] = false;
				if (cur == 0 && n % 2 != 0 && nd[cur] == n / 2)
				{
					dfs(cur + 1, (n + 1) / 2);
 
				}
				else
					dfs(cur + 1, n);
				//回退
				l_r[lr] = true;
				r_l[rl] = true;
 
			}
			swap(nd[cur], nd[i]);
		}
	}
}
int main()
{
	//freopen("debug.txt", "r", stdin);
	int N;
	cout << "請輸入測試數據個數:";
	cin >> N;
	while (N--)
	{
		cout << "請輸入皇后的個數:";
		cin >> n;
		cout << "請選擇是否要輸出所有排列(’1‘ or ‘0’,‘1’表示輸出,‘0’表示不輸出):" << endl;
		cin >> p;
		init();
		dfs(0,(n+1)/2);
		cout << "共有" << num<< "種方案" << endl;
	}
	return 0;
}



測試用例及運行結果:
皇后的個數:2 5
方案個數 :0 10
在這裏插入圖片描述

“別人能做到的事,我一定也能做到”

關注公衆號【輕鬆玩編程】回覆關鍵字“電子書”,“計算機資源”,“Java從入門到進階”,”JavaScript教程“,“算法”,“Python學習資源”,“人工智能”等即可獲取學習資源

在這裏插入圖片描述

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