日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

n皇后问题(回溯法-递归法和循环法,最小冲突法(较快解决10000级别问题))

發(fā)布時間:2025/4/16 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 n皇后问题(回溯法-递归法和循环法,最小冲突法(较快解决10000级别问题)) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • n皇后問題簡單解釋
    • 遞歸版本
    • 非遞歸版本-找到一個可行解版本
    • 非遞歸版本-找到所有可行解版本
    • 非遞歸版本二
    • 最小沖突算法--5000級別較快
    • 最小沖突算法改進
    • 關(guān)于更高性能的算法

n皇后問題簡單解釋

對于一個n*n的棋盤來說,皇后如果是同行同列或者是在同一斜對角上,就是會相互擊殺。

所以,我們需要找到一個安全的(使得所有皇后之間不相互擊殺)安排方式。

遞歸版本

#include <iostream> using namespace std; #include <cmath> int n; bool place(int x[], int k) {for (int i = 1; i < k; ++i)if (x[i] == x[k] || (abs(x[i] - x[k]) == abs(i - k))) return false;return true; }void n_queens(int k, int x[]) {if (k > n) {for (int i = 1; i <= n; ++i) cout << x[i] << " ";cout << endl;}else {for (int i = 1; i <= n; ++i) {x[k] = i;if (place(x, k)) n_queens(k + 1, x);}} } int main() {cin >> n;int *x = new int[n+1];n_queens(1, x);delete[]x;system("pause"); }

輸入一個n,就可以輸出對應(yīng)的n皇后的安排方式。

4 2 4 1 3 3 1 4 2

上面的數(shù)組含義,即,在第i行皇后在第x[i]列。

非遞歸版本-找到一個可行解版本

#include <iostream> using namespace std; #include <cmath> int n; bool place(int x[], int k) {for (int i = 1; i < k; ++i)if (x[i] == x[k] || (abs(x[i] - x[k]) == abs(i - k))) return false;return true; }void n_queens(int k, int x[]) {k = 1;x[1] = 0;while (k > 0) {x[k] ++;while ((x[k] <= n) && (!place(x, k))) x[k]++;if (x[k] <= n) {if (k == n) break; // 找到了一種解else {x[++k] = 0;}}else {x[k] = 0; k--; // 回溯 這條路走不通}}for (int i = 1; i <= n; ++i) cout << x[i] << " ";cout << endl; } int main() {cin >> n;int *x = new int[n+1];n_queens(1, x);delete[]x;system("pause"); }

運行情況為

4 2 4 1 3

非遞歸版本-找到所有可行解版本

#include <iostream> using namespace std; #include <cmath> int n; bool place(int k, int x[]) {for (int i = 1; i < k; ++i)if (x[i] == x[k] || (abs(x[i] - x[k]) == abs(i - k))) return false;return true; }void n_queens(int x[]) {for (int m = 1; m <= n; ++m) x[m] = 0;int i = 1, j;while (i <= n) {j = x[i] + 1;while (j <= n) {x[i] = j; //將第i行的皇后放在第j列上if (place(i, x)) break; // 跳出else j++; // 尋找下一個}if (j == n + 1) { // 在這一行,找了所有列都不對 if (i == 1) break; // 找遍了所有方法 都沒有結(jié)果else i--; // 回溯到上一行} else if (i == n) { // 全部找到了for (int m = 1; m <= n; ++m) cout << x[m] << " ";cout << endl;}else { x[++i] = 0; }} } int main() {cin >> n;int *x = new int[n + 1];n_queens(x);delete[]x;system("pause"); }

運行的結(jié)果;

4 2 4 1 3 3 1 4 2

非遞歸版本二

#include <iostream> using namespace std; #include <cmath> int n; int *x; bool place(int x[], int k) {for (int i = 1; i < k; ++i)if (x[i] == x[k] || (abs(x[i] - x[k]) == abs(i - k))) return false;return true; }void iterativeBacktrack() {int t = 1;int *f = new int[n + 1];for (int i = 1; i <= n; ++i) f[i] = 1;while (t > 0) {if (f[t] <= n) {while (f[t] <= n){x[t] = f[t];if (place(x, t)) { // 第k位放的沒有問題f[t]++;if (t == n) { // outputfor (int m = 1; m <= n; ++m) cout << x[m] << " ";cout << endl;}else { t++; // 考慮下一行的皇后位置break;}} else f[t]++; }}else { f[t] = 1; t--;}}delete[] f; }int main() {cin >> n;x = new int[n + 1];iterativeBacktrack();delete[]x;system("pause"); }

最小沖突算法–5000級別較快

(只能找到一個解)

學習了下面這個鏈接的代碼,做了很詳細的筆記
https://www.cnblogs.com/fstang/archive/2013/05/12/3073598.html

  • 解決n=5000的數(shù)據(jù) 20s
  • 解決n=6000的數(shù)據(jù) 80s
#include <iostream> using namespace std; #include <ctime> #define MAX 6000 //最多可能皇后數(shù) #define swap(a,b) {int t = a; a = b; b = t;} //row[i]表示當前擺放方式下第i行的皇后數(shù),col[i]表示當前擺放方式下第i列的皇后數(shù) int row[MAX]; int col[MAX];int N; //放置N個皇后在N*N棋盤上//從左上到右下的對角線上row-col值是相同的,但是這個值有可能是負值,最小為-(N-1),//所以可以做個偏移,統(tǒng)一加上N-1,這樣這個值就在[0,2*N-2]范圍內(nèi),將這個值作為該對角線的編號//pdiag[i]表示當前擺放方式下編號為i的對角線上的皇后數(shù) int pdiag[2 * MAX];//principal diagonal,主對角線,左上到右下(表示和主對角線平行的2N-1條對角線)//從右上到左下的對角線row+col的值相同,取值范圍為[0, 2 * MAX - 2],作為對角線編號//cdiag[i]表示編號為i的對角線上的皇后數(shù) int cdiag[2 * MAX];//counter diagonal,副對角線//R[]用來存儲皇后放置位置,R[row] = col表示(row,col)處,即“第row行第col列”有個皇后 int R[MAX];//給定二維矩陣的一個點坐標,返回其對應(yīng)的左上到右下的對角線編號 int getP(int row, int col) {return row - col + N - 1; }//給定二維矩陣的一個點坐標,返回其對應(yīng)的右上到左下的對角線編號 int getC(int row, int col) {return row + col; }//返回begin, begin + 1, ... , end - 1 這end - begin個數(shù)中的隨機的一個 int my_rand(int begin, int end) {//左閉右開[begin, end)return rand() % (end - begin) + begin; }//原地shuffle算法,算法導論中的randomize in place算法 void randomize(int a[], int begin, int end)// 左閉右開 {for (int i = begin; i <= end - 2; i++) {int x = my_rand(i, end);swap(a[i], a[x]);} }//初始化皇后的擺放,同時初始化row,col,pdiag,cdiag數(shù)組 void init() {for (int i = 0; i < N; i++) {//N queensR[i] = i;}randomize(R, 0, N);//初始化N個皇后對應(yīng)的R數(shù)組為0~N-1的一個排列,即沒有任意皇后同列,也沒有任何皇后同行for (int i = 0; i < N; i++) {row[i] = 1;//每行恰好一個皇后col[i] = 0;//下面再做初始化的}for (int i = 0; i < 2 * N - 1; i++) {//N queenspdiag[i] = 0;cdiag[i] = 0;}for (int i = 0; i < N; i++) {//N queenscol[R[i]]++;pdiag[getP(i, R[i])]++;cdiag[getC(i, R[i])]++;} }bool adjust_row(int row); // 調(diào)整下第row行的皇后的位置 void print_result(); // 輸出結(jié)果 bool qualify(); //驗證是否正確int main(int argc, const char *argv[]) {srand((unsigned)time(NULL));// 設(shè)置好隨機數(shù)種子cin >> N;init();if (!qualify()) { // 如果初始表不滿足的話bool can_terminate = false;while (!can_terminate) {for (int i = 0; i < N; i++) {if (adjust_row(i)) {can_terminate = true;break;}}}}print_result();system("pause");return 0; } //用最小沖突算法調(diào)整第row行的皇后的位置(初始化時每行都有一個皇后,調(diào)整后仍然在第row行) //調(diào)整過后check一下看看是否已經(jīng)沒有沖突,如果沒有沖突(達到終止狀態(tài)),返回true bool adjust_row(int row) {int cur_col = R[row]; // 通過行得到列int optimal_col = cur_col;// 最佳列號,設(shè)置為當前列,然后更新int min_conflict = col[optimal_col] // col不減一的原因是交換任然保證每行每列都有一個而已+ pdiag[getP(row, optimal_col)] - 1 // 現(xiàn)在還沒移過去+ cdiag[getC(row, optimal_col)] - 1; // 對角線沖突數(shù)為當前對角線皇后數(shù)減一for (int i = 0; i < N; i++) {//逐個檢查第row行的每個位置if (i == cur_col) continue; // 重復就跳過了int conflict = col[i] + pdiag[getP(row, i)] + cdiag[getC(row, i)];if (conflict < min_conflict) { // 因為之前這個點還沒有移動,所以到這之后肯定是還會min_conflict = conflict;optimal_col = i;}}if (optimal_col != cur_col) {//要更新col,pdiag,cdiagcol[cur_col]--;pdiag[getP(row, cur_col)]--;cdiag[getC(row, cur_col)]--;col[optimal_col]++;pdiag[getP(row, optimal_col)]++;cdiag[getC(row, optimal_col)]++;R[row] = optimal_col;// 注意,這里我們沒有移動 在這個列上的之前的那個行上的皇后if (col[cur_col] == 1 && col[optimal_col] == 1&& pdiag[getP(row, optimal_col)] == 1 && cdiag[getC(row, optimal_col)] == 1) {return qualify();//qualify相對更耗時,所以只在滿足上面基本條件后才檢查}}//當前點就是最佳點,一切都保持不變return false;//如果都沒變的話,肯定不滿足終止條件,否則上一次就應(yīng)該返回true并終止了 } bool qualify() {for (int i = 0; i < N; i++) {//N queensif (col[R[i]] != 1 ||pdiag[getP(i, R[i])] != 1 ||cdiag[getC(i, R[i])] != 1) {return false;}}return true; } void print_result() {cout << "the result is like this:\n";for (int i = 0; i < N; i++) {cout << R[i] << " ";}cout << endl;/*for (int j = 0; j < N; j++) {for (int k = 0; k < N; k++) {if (R[j] == k)cout << "*";elsecout << "-";}cout << endl;}*/ }

最小沖突算法改進

根據(jù)觀測,有兩個函數(shù)經(jīng)常調(diào)用,但是非常短。由于函數(shù)調(diào)用本身設(shè)計到壓棧一直都

  • 解決1w的數(shù)據(jù)需要6秒
#include <iostream> using namespace std; #include <ctime> #define MAX 1000005 //最多可能皇后數(shù) #define swap(a,b) {int t = a; a = b; b = t;} #define getP(row, col) (row - col + N - 1) #define getC(row, col) (row + col) //row[i]表示當前擺放方式下第i行的皇后數(shù),col[i]表示當前擺放方式下第i列的皇后數(shù) int row[MAX]; int col[MAX];int N; //放置N個皇后在N*N棋盤上//從左上到右下的對角線上row-col值是相同的,但是這個值有可能是負值,最小為-(N-1),//所以可以做個偏移,統(tǒng)一加上N-1,這樣這個值就在[0,2*N-2]范圍內(nèi),將這個值作為該對角線的編號//pdiag[i]表示當前擺放方式下編號為i的對角線上的皇后數(shù) int pdiag[2 * MAX];//principal diagonal,主對角線,左上到右下(表示和主對角線平行的2N-1條對角線)//從右上到左下的對角線row+col的值相同,取值范圍為[0, 2 * MAX - 2],作為對角線編號//cdiag[i]表示編號為i的對角線上的皇后數(shù) int cdiag[2 * MAX];//counter diagonal,副對角線//R[]用來存儲皇后放置位置,R[row] = col表示(row,col)處,即“第row行第col列”有個皇后 int R[MAX];給定二維矩陣的一個點坐標,返回其對應(yīng)的左上到右下的對角線編號 //int getP(int row, int col) { // return row - col + N - 1; //} // 給定二維矩陣的一個點坐標,返回其對應(yīng)的右上到左下的對角線編號 //int getC(int row, int col) { // return row + col; //}//返回begin, begin + 1, ... , end - 1 這end - begin個數(shù)中的隨機的一個 int my_rand(int begin, int end) {//左閉右開[begin, end)return rand() % (end - begin) + begin; }//原地shuffle算法,算法導論中的randomize in place算法 void randomize(int a[], int begin, int end)// 左閉右開 {for (int i = begin; i <= end - 2; i++) {int x = my_rand(i, end);swap(a[i], a[x]);} }//初始化皇后的擺放,同時初始化row,col,pdiag,cdiag數(shù)組 void init() {for (int i = 0; i < N; i++) {//N queensR[i] = i;}randomize(R, 0, N);//初始化N個皇后對應(yīng)的R數(shù)組為0~N-1的一個排列,即沒有任意皇后同列,也沒有任何皇后同行for (int i = 0; i < N; i++) {row[i] = 1;//每行恰好一個皇后col[i] = 0;//下面再做初始化的}for (int i = 0; i < 2 * N - 1; i++) {//N queenspdiag[i] = 0;cdiag[i] = 0;}for (int i = 0; i < N; i++) {//N queenscol[R[i]]++;pdiag[getP(i, R[i])]++;cdiag[getC(i, R[i])]++;} }bool adjust_row(int row); // 調(diào)整下第row行的皇后的位置 void print_result(); // 輸出結(jié)果 bool qualify(); //驗證是否正確int main(int argc, const char *argv[]) {srand((unsigned)time(NULL));// 設(shè)置好隨機數(shù)種子clock_t startTime, endTime;cin >> N;startTime = clock();init();if (!qualify()) { // 如果初始表不滿足的話bool can_terminate = false;while (!can_terminate) {for (int i = 0; i < N; i++) {if (adjust_row(i)) {can_terminate = true;break;}}}}endTime = clock();print_result();cout << "用時:" << 1.0*(endTime - startTime) / CLOCKS_PER_SEC <<"秒"<< endl;system("pause");return 0; } //用最小沖突算法調(diào)整第row行的皇后的位置(初始化時每行都有一個皇后,調(diào)整后仍然在第row行) //調(diào)整過后check一下看看是否已經(jīng)沒有沖突,如果沒有沖突(達到終止狀態(tài)),返回true bool adjust_row(int row) {int cur_col = R[row]; // 通過行得到列int optimal_col = cur_col;// 最佳列號,設(shè)置為當前列,然后更新int min_conflict = col[optimal_col] // col不減一的原因是交換任然保證每行每列都有一個而已+ pdiag[getP(row, optimal_col)] - 1 // 現(xiàn)在還沒移過去+ cdiag[getC(row, optimal_col)] - 1; // 對角線沖突數(shù)為當前對角線皇后數(shù)減一for (int i = 0; i < N; i++) {//逐個檢查第row行的每個位置if (i == cur_col) continue; // 重復就跳過了int conflict = col[i] + pdiag[getP(row, i)] + cdiag[getC(row, i)];if (conflict < min_conflict) { // 因為之前這個點還沒有移動,所以到這之后肯定是還會min_conflict = conflict;optimal_col = i;}}if (optimal_col != cur_col) {//要更新col,pdiag,cdiagcol[cur_col]--;pdiag[getP(row, cur_col)]--;cdiag[getC(row, cur_col)]--;col[optimal_col]++;pdiag[getP(row, optimal_col)]++;cdiag[getC(row, optimal_col)]++;R[row] = optimal_col;// 注意,這里我們沒有移動 在這個列上的之前的那個行上的皇后if (col[cur_col] == 1 && col[optimal_col] == 1&& pdiag[getP(row, optimal_col)] == 1 && cdiag[getC(row, optimal_col)] == 1) {return qualify();//qualify相對更耗時,所以只在滿足上面基本條件后才檢查}}//當前點就是最佳點,一切都保持不變return false;//如果都沒變的話,肯定不滿足終止條件,否則上一次就應(yīng)該返回true并終止了 } bool qualify() {for (int i = 0; i < N; i++) {//N queensif (col[R[i]] != 1 ||pdiag[getP(i, R[i])] != 1 ||cdiag[getC(i, R[i])] != 1) {return false;}}return true; } void print_result() {cout << "the result is like this:\n";for (int i = 0; i < N; i++) {cout << R[i] << " ";}cout << endl;/*for (int j = 0; j < N; j++) {for (int k = 0; k < N; k++) {if (R[j] == k)cout << "*";elsecout << "-";}cout << endl;}*/ }

關(guān)于更高性能的算法

下面的這個算法,是可以在20s內(nèi)接觸10w的數(shù)據(jù)規(guī)模的。
https://blog.csdn.net/yongnuzhibu/article/details/7178112

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的n皇后问题(回溯法-递归法和循环法,最小冲突法(较快解决10000级别问题))的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。