文章目錄
- n皇后問題簡單解釋
- 遞歸版本
- 非遞歸版本-找到一個可行解版本
- 非遞歸版本-找到所有可行解版本
- 非遞歸版本二
- 最小沖突算法--5000級別較快
- 最小沖突算法改進
- 關于更高性能的算法
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,就可以輸出對應的n皇后的安排方式。
4
2 4 1 3
3 1 4 2
上面的數組含義,即,在第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
; if (place(i
, x
)) break; else j
++; }if (j
== n
+ 1) { if (i
== 1) break; 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");
}
運行的結果;
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
)) { f
[t
]++;if (t
== n
) { for (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的數據 20s
- 解決n=6000的數據 80s
#include <iostream>
using namespace std
;
#include <ctime>
#define MAX 6000
#define swap(a,b) {int t = a; a = b; b = t;}
int row
[MAX
];
int col
[MAX
];int N
;
int pdiag
[2 * MAX
];
int cdiag
[2 * MAX
];
int R
[MAX
];
int getP(int row
, int col
) {return row
- col
+ N
- 1;
}
int getC(int row
, int col
) {return row
+ col
;
}
int my_rand(int begin
, int end
) {return rand() % (end
- begin
) + begin
;
}
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
]);}
}
void init()
{for (int i
= 0; i
< N
; i
++) {R
[i
] = i
;}randomize(R
, 0, N
);for (int i
= 0; i
< N
; i
++) {row
[i
] = 1;col
[i
] = 0;}for (int i
= 0; i
< 2 * N
- 1; i
++) {pdiag
[i
] = 0;cdiag
[i
] = 0;}for (int i
= 0; i
< N
; i
++) {col
[R
[i
]]++;pdiag
[getP(i
, R
[i
])]++;cdiag
[getC(i
, R
[i
])]++;}
}bool
adjust_row(int row
);
void print_result();
bool
qualify(); int main(int argc
, const char *argv
[])
{srand((unsigned)time(NULL));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;
}
bool
adjust_row(int row
) {int cur_col
= R
[row
]; int optimal_col
= cur_col
;int min_conflict
= col
[optimal_col
] + pdiag
[getP(row
, optimal_col
)] - 1 + cdiag
[getC(row
, optimal_col
)] - 1; for (int i
= 0; i
< N
; i
++) {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
[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();}}return false
;
}
bool
qualify() {for (int i
= 0; i
< N
; i
++) {if (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
;
}
最小沖突算法改進
根據觀測,有兩個函數經常調用,但是非常短。由于函數調用本身設計到壓棧一直都
#include <iostream>
using namespace std
;
#include <ctime>
#define MAX 1000005
#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)
int row
[MAX
];
int col
[MAX
];int N
;
int pdiag
[2 * MAX
];
int cdiag
[2 * MAX
];
int R
[MAX
];
int my_rand(int begin
, int end
) {return rand() % (end
- begin
) + begin
;
}
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
]);}
}
void init()
{for (int i
= 0; i
< N
; i
++) {R
[i
] = i
;}randomize(R
, 0, N
);for (int i
= 0; i
< N
; i
++) {row
[i
] = 1;col
[i
] = 0;}for (int i
= 0; i
< 2 * N
- 1; i
++) {pdiag
[i
] = 0;cdiag
[i
] = 0;}for (int i
= 0; i
< N
; i
++) {col
[R
[i
]]++;pdiag
[getP(i
, R
[i
])]++;cdiag
[getC(i
, R
[i
])]++;}
}bool
adjust_row(int row
);
void print_result();
bool
qualify(); int main(int argc
, const char *argv
[])
{srand((unsigned)time(NULL));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;
}
bool
adjust_row(int row
) {int cur_col
= R
[row
]; int optimal_col
= cur_col
;int min_conflict
= col
[optimal_col
] + pdiag
[getP(row
, optimal_col
)] - 1 + cdiag
[getC(row
, optimal_col
)] - 1; for (int i
= 0; i
< N
; i
++) {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
[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();}}return false
;
}
bool
qualify() {for (int i
= 0; i
< N
; i
++) {if (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
;
}
關于更高性能的算法
下面的這個算法,是可以在20s內接觸10w的數據規模的。
https://blog.csdn.net/yongnuzhibu/article/details/7178112
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的n皇后问题(回溯法-递归法和循环法,最小冲突法(较快解决10000级别问题))的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。