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;
}