1. 程式人生 > 程式設計 >N皇后問題暴力解和回溯解問題分析和演演算法實現-leetcode困難難度

N皇后問題暴力解和回溯解問題分析和演演算法實現-leetcode困難難度

n皇后問題是經典的回溯解題的案例,回溯一般用在有多個解的演演算法中,回溯的核心是窮舉,一般通過必要的減枝提高效率(減少重複計算等),得到一個解後,把當前解進行儲存,然後將當前解標記為未解決,繼續嘗試下一個可能滿足條件的解,即回溯

窮舉解有利於理解問題的本質,回溯解提高解題效率

題目參考:leetcode-cn.com/problems/n-…

可以看到n皇后是leetcode上一道難度為困難的題

基本事實

  1. 皇后的個數和行數相等

  2. 每行必定有且只有一個皇后,如果用row1,row2表示兩個皇后所處的行,必定存在row1 != row2

  3. 兩個皇后必定存在不在同一條斜線上,[row1,col1],[row2,col2],

    必定存在 | row1 - row2 | != | col1 -col2 |

    或者 左右對角線分開判斷 左對角線 row1 - col1 == row2 - col2 右對角線 row1 + col1 = row2 + col2

  4. 兩個皇后必定不在同一列上,必定存在col1 != col2

  5. 要找到所有皇后的集合,必定在找到滿足的一個皇后後,不能停,需要繼續查詢下一個皇后

滿足以上條件,即可求得n皇后的解

回溯解

#include <iostream>
#include <vector>
#include <cmath> 

using namespace
std; #define QUEUE_NUM 8 int usedRowAndCol[QUEUE_NUM + 1] = {0}; // 已經使用的行和列,0表示未使用,有值表示列值,鍵是行 值是列 vector< vector<string> > result; // 儲存n皇后所有可能結果 void findAQueue(int); void recordAnswer(); void printAnswer(); void findAQueue(int row) { // row這行肯定是可以用的,主要是獲取可用的列,列也有8列,所以進行窮舉,判斷row行的col列是否可用
for (int col = 1; col <= QUEUE_NUM; col ++) { // 檢查列是否可用 1. 兩個皇后必定不在同一列上,必定存在col1 != col2 // 2. 兩個皇后必定存在不在同一條斜線上,col2],必定存在 | row1 - col1 | != | row2 -col2 | // 判斷當前行的,當前col列在不在對角線上已經佔用了 bool canPlaceInThisCol = true; for (int currentRow = 1; currentRow < row; currentRow ++) { // currentRow行的col列用過了 if (usedRowAndCol[currentRow] == col) { canPlaceInThisCol = false; break; } // 左對角線有用過了 if (currentRow - usedRowAndCol[currentRow] == row - col) { canPlaceInThisCol = false; break; } // 右對角線是否用過了 if (currentRow + usedRowAndCol[currentRow] == row + col) { canPlaceInThisCol = false; break; } } if (canPlaceInThisCol) { // 列可用,進行記錄 usedRowAndCol[row] = col; // 找下一行的那列可以使用 if (row < QUEUE_NUM) { findAQueue(row + 1); } if (row == QUEUE_NUM) { // 如果當前是最後一行了,就不用再繼續找了,記錄答案 recordAnswer(); } } } } void recordAnswer() { vector<string> oneResult; for (int i = 1; i <= QUEUE_NUM; i ++) { string row(QUEUE_NUM,'.'); row[usedRowAndCol[i] - 1] = 'Q'; oneResult.push_back(row); } result.push_back(oneResult); } void printAnswer() { for (int i = 0; i < result.size(); i ++) { for (int j = 0; j < result[i].size(); j ++) { cout << result[i][j] << endl; } cout << "---------sep----------" << endl; } } int main(int argc,char *argv[]) { findAQueue(1); cout << result.size() << endl; } 複製程式碼

這裡為了提高程式碼的可讀性,使用了分別排除左對角線和右對角線的方法排除皇后,也可以直接使用絕對值方法

把程式碼稍作修改填到leetcode上即可

暴力解

排列出所有的組合,挨個判斷每個組合是否滿足n皇后的條件

#include <iostream>
#include <vector>

using namespace std;

#define QUEUE_NUM 4

int QUEUE_LIST[QUEUE_NUM];

vector< vector<string> > result; // 儲存n皇后所有可能結果

bool is_ok(int row)
{
	if (row == QUEUE_NUM - 1) {
		return true;
	}
	
	int first = QUEUE_LIST[row];
	bool ok = true;

	for (int currentRow = row + 1; currentRow < QUEUE_NUM; currentRow ++) {
		// 不能在同一列
		if (QUEUE_LIST[currentRow] == first) {
			ok = false;
			break;
		}
		// 左對角線有用過了
		if (currentRow - QUEUE_LIST[currentRow] == row - first) {
			ok = false;
			break;
		}
		// 右對角線是否用過了
		if (currentRow + QUEUE_LIST[currentRow] == row + first) {
			ok = false;
			break;
		}
	}
	
	// 檢查 row + 1行和 row + 2 -> QUEUE_NUM行是否滿足條件
	if (ok) {
		ok = is_ok(row + 1);
	}
	
	return ok;
}

void recordAnswer()
{
	vector<string> oneResult;
	for (int i = 0; i < QUEUE_NUM; i ++) {
		string row(QUEUE_NUM,'.');
		row[QUEUE_LIST[i]] = 'Q';
		
		oneResult.push_back(row);
	}
	result.push_back(oneResult);
}

void printAnswer()
{
	for (int i = 0; i < result.size(); i ++) {
		for (int j = 0; j < result[i].size(); j ++) {
			cout << result[i][j] << endl;
		}
		cout << "---------sep----------" << endl;
	}
}

void queue(int row)
{
	for (int i = 0; i < QUEUE_NUM; i ++) {
		QUEUE_LIST[row] = i;
		if (row == QUEUE_NUM - 1) {
			// 找到一個組合,進行判斷是否滿足8皇后定義
			if (is_ok(0)) {
				// 進行記錄
				recordAnswer();
			}
			continue;
		}
		queue(row + 1);
	}
}

int main(int argc,char *argv[]) {
	queue(0);
	printAnswer();
}
複製程式碼

由於窮舉解多出了很多無效的組合,lc上直接超時了...,可以在本地計算機上自行驗證

一些注意的點

暴力解是先求出所有的皇后的組合,然後逐個判斷皇后是否滿足位置條件

回溯解就是在求皇后組合是否滿足條件時,提前進行判斷,提前剪掉了這部分皇后的組合

回溯法的col,row值從1開始是為了避免usedRowAndCol預設值的0和col,row索引的0衝突,導致的判斷異常

參考資料

  1. www.cplusplus.com/reference/c…
  2. zh.wikipedia.org/wiki/八皇后問題