標籤:blog os 2014 問題 for 代碼
(一)八皇后問題
(1)回溯法
#include <iostream>#include <string>#define MAXN 100using namespace std;int tot = 0, n = 8;int C[MAXN];void search(int cur) {if(cur == n) ++tot; //遞迴邊界,只要走到了這裡,所有皇后必然不衝突else for(int i = 0; i < n; ++i) {int ok = 1;C[cur] = i; //嘗試把第cur行的皇后放在第i列for(int j = 0; j < cur; ++j) { //檢查是否和前面的皇后衝突if(C[cur] == C[j] || cur-C[cur] == j-C[j] || cur+C[cur] == j+C[j]) {ok = 0;break;}}if(ok) search(cur+1); //如果合法,則繼續遞迴}}int main() {search(0);cout << tot << endl;return 0;}
(2)利用二維數組最佳化的回溯法
#include <iostream>#include <string>#define MAXN 100using namespace std;int tot = 0, n = 8;int vis[3][MAXN], C[MAXN]; void search(int cur) {if(cur == n) ++tot;else for(int i = 0; i < n; ++i) {if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) { //利用二維數組直接判斷C[cur] = i;vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1; //修改全域變數search(cur+1);vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0; //這裡一定要改回來!}}}int main() {memset(vis, 0, sizeof(vis));search(0);cout << tot << endl;return 0;}
在上面的程式中,vis數組表示已經放置的皇后佔據了哪些列、主對角線和副對角線。
一般的在回溯法中,如果修改了全域變數vis數組,那麼遞迴調用結束後一定要修改回來!因為在解答樹中,如果下一層不滿足條件,那麼就需要回溯,那麼就要把修改過的vis給改回來,那樣,才能繼續進行下一次的判斷!!!
(二)素數環
題目:輸入正整數n,把整數1,2,3,...,n組成一個環,使得相鄰兩個整數之和均為素數。輸出時從整數1開始逆時針排列。同一個環應該恰好輸出一次。
典型的回溯法,代碼如下:
#include <iostream>#include <algorithm>using namespace std;const int MAXN = 1000;int isp[MAXN], vis[MAXN], A[MAXN], n;int is_prime(int x) { //判斷一個數是否為素數for(int i = 2; i*i <= x; ++i) {if(x % i == 0) return 0;}return 1;}void dfs(int cur) {if(cur == n && isp[A[0] + A[n-1]]) {for(int i = 0; i < n; ++i) cout << A[i] << " ";cout << endl;}else {for(int i = 2; i <= n; ++i) {if(!vis[i] && isp[i + A[cur-1]]) {A[cur] = i; //數字i滿足條件,所以第cur個位置可以放數字ivis[i] = 1;dfs(cur+1);vis[i] = 0; //跟上題一樣,一定不能忘記把vis的值改回來,原因見上一題的代碼注釋}}}}int main() {memset(vis, 0, sizeof(vis)); //遞迴調用之前,一定要把vis函數清0cin >> n;for(int i = 2; i <= 2*n; ++i) isp[i] = is_prime(i); //先判斷一個數是不是素數,便於後期判斷A[0] = 1; //題目規定第一個數字從1開始dfs(1); //所以遞迴調用是用位置1開始,而不是從位置0開始,因為第一個位置的數字已經確定了是1return 0;}