1. 程式人生 > 實用技巧 >DFS入門——素數環問題

DFS入門——素數環問題

題目出處:《資訊學奧賽一本通》例5.1。

題目描述

素數環:從 \(1\)\(n(2 \le n \le 20)\)\(n\) 個數擺成一個環,要求相鄰的兩個數的和是一個素數。

輸入格式

輸入包含一個整數 \(n(2 \le n \le 20)\)

輸出格式

按字典序從小到大的順序輸出所有排列方案,每個排列方案佔一行。每行的 \(n\) 個數之間由一個空格分隔。

樣例輸入

2

樣例輸出

1 2
2 1

問題分析

很明顯,這是一道可以用搜索解決的問題,我們可以採用“回溯”思想,使用深度優先搜尋解決這個問題。
我們用 ans[] 陣列來存放我們當前遍歷到的答案, ans[id] 用於表示當前排列的第 id 個數是什麼。所以我們可以開一個函式 void f(int id)

來表示要在第 id 個位置放數,我只需要從 1 到 n 遍歷每一個數(我這裡假設是 i),並判斷 i 是否能放。
在第 id 個位置能放 i 當且僅當:

  • \(ans[1]\)\(ans[id-1]\) 都不等於 \(i\),即 \(i\) 之前沒有放過;
  • \(id \gt 1\) 時,滿足 \(ans[id-1]+ans[id]\) 是素數;
  • \(id = n\) 時,滿足 \(ans[1] + ans[n]\) 是素數。

這樣,我們遞迴地呼叫 f(id) ,當 id>n 時就是我們遞迴的邊界條件;一旦 id>n 就說明我找到了一種方案。
實現程式碼如下:

#include<bits/stdc++.h>
using namespace std;
int ans[22], n;
bool isp(int a) {   // 判斷a是否是素數
    if (a < 2) return false;
    for (int i = 2; i * i <= a; i ++) if (a%i==0) return false;
    return true;
}
void output() {     // 輸出一種排列方案
    for (int i = 1; i <= n; i ++)
        cout << (i>1 ? " " : "") << ans[i];
    cout << endl;
}
void f(int id) {    // 搜尋函式,在第id個位置嘗試放上一個數
    if (id > n) {   // 邊界條件
        if (isp(ans[1]+ans[n])) output();
        return;
    }
    for (int i = 1; i <= n; i ++) { // 遍歷i = 1 to n ,看看第id個位置能否放i
        bool flag = true;
        if (id > 1 && !isp(ans[id-1]+i)) flag = false;
        if (flag) {
            for (int j = 1; j < id; j ++)
                if (ans[j] == i) {
                    flag = false;
                    break;
                }
        }
        if (flag) {
            ans[id] = i;
            f(id+1);
        }
    }
}
int main() {
    cin >> n;
    f(1);
    return 0;
}