藍橋杯-N皇后

DawnTraveler發表於2024-04-10

0.題目

【題目描述】

有一個N*N的矩陣棋盤和N個棋子,現在需要將N個棋子按要求放置在矩陣方格中。

要求:

1、任意兩顆棋子不能在同一行

2、任意兩個棋子不能在同一列

3、任意兩個棋子不能在同一對角線上(下面的紅線都是對角線)

根據以上要求,問N個棋子放置到N*N矩陣中有多少种放置方案?

【輸入描述】

輸入一個正整數N(1<N<11),表示N*N的矩陣方格和N個棋子數量。

【輸出描述】

輸出N個棋子按要求放置到N*N的矩陣中,有多少种放置方案?

1.題解

1.1 DFS搜尋

思路

對於不能同行,整體按行順序從上到下進行遍歷(外層迴圈)
難點主要是糾正同列和對角線的情況,利用記憶化儲存儲存之前節點實際皇后擺放位置,然後和當前皇后位置進行比較
如果發生衝突,放棄該位置; 如果不發生衝突, 可以選擇該位置,也可以不選擇該位置,同時更新當前chosen陣列的值即可

程式碼

#include<bits/stdc++.h>
using namespace std;

// 表示第n行的皇后擺放的列位置
int chosen[11] = {-1};
int ans = 0;
int n;

// x表示當前已經選擇了x個
void DFS(int x) {
	if (x == n + 1) {
		ans++;
		return;
	}

//	// 選擇該行放置的列位置i, [x][i],
//	for (int i = 1; i <= n; i++) {
//		if(x == 1) {
//			chosen[x] = i;
//			DFS(x + 1);
//		} else {
//			// 遍歷之前的行, [j][chosen[j]]
//			for (int j = 1; j < x; j++) {
//				if ((chosen[j] != i) && (abs(chosen[j] - i) != abs(x - j))) {
//					// 選擇該位置
//					chosen[x] = i;
//					DFS(x + 1);
//					// 不選擇該位置
//					chosen[x] = -1;
//				}
//			}
//		}
//	}

	// 選擇該行放置的列位置i, [x][i],
	for (int i = 1; i <= n; i++) {
		bool conflict = false;
		// 遍歷之前的行,判斷有無衝突 [j][chosen[j]]
		for (int j = 1; j < x; j++) {
			if((chosen[j] == i) || (abs(j - x) == abs(chosen[j] - i))){
				conflict = true;
				break;
			} 
		}
		// 沒有衝突 
		if(!conflict){
			// 選擇該數
			chosen[x] = i;
			DFS(x+1);
			// 不選擇該數 
			chosen[x] = -1;
		}

	}
}

int main() {
	cin >> n;
	DFS(1);
	cout << ans;
	return 0;
}

最佳化版本

主要是封裝了函式,使其更符合普遍模板

#include <bits/stdc++.h>
using namespace std;

int chosen[11] = {0};
int ans = 0;
int n;

// 判斷是否會發生衝突(剪枝) 
int PD(int k){
	for(int i = 1; i < k; i++){
		if(abs(k-i) == abs(chosen[k]-chosen[i])){
			return 0;
		} else if(chosen[k] == chosen[i])
			return 0;
	}
	return 1;
} 

// 判斷返回條件 
bool check(int a){
	if(a > n){
		ans++;
	}
	else return 0;
	return 1;
} 

void DFS(int x) {
    if (check(x)) {
		return;
    }
    
    // 嘗試在第 x 行的每一列放置皇后 [x][i];
    for (int i = 1; i <= n; i++) {
		chosen[x] = i; 
		// 判斷這一步是否衝突
		if(PD(x)) {
			DFS(x+1);
		}else{
			// 不需要清零,每次開頭的chosen[x] = i;就可以實現重新整理 
//			chosen[x] = 0;
			continue;
		}
    }
}

int main() {
    cin >> n;
    DFS(1);
    cout << ans << endl;
    return 0;
}