1. 程式人生 > >nyoj ACM:素數環(DFS 回溯 遞迴)

nyoj ACM:素數環(DFS 回溯 遞迴)

素數環
時間限制:1000 ms | 記憶體限制:65535 KB
難度:2
描述
有一個整數n,把從1到n的數字無重複的排列成環,且使每相鄰兩個數(包括首尾)的和都為素數,稱為素數環。

為了簡便起見,我們規定每個素數環都從1開始。例如,下圖就是6的一個素數環。
這裡寫圖片描述
輸入
有多組測試資料,每組輸入一個n(0<n<20),n=0表示輸入結束。
輸出
每組第一行輸出對應的Case序號,從1開始。
如果存在滿足題意敘述的素數環,從小到大輸出。
否則輸出No Answer。
樣例輸入
6
8
3
0
樣例輸出
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4
Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2
Case 3:
No Answer
來源
hdu改編
上傳者
ACM_丁國強

思路:

素數環(還有後面 部分和問題)都是用來回溯演算法的思想,而且回溯演算法使用 dfs 實現的, 而dfs是用遞迴 寫得(還有剪枝)。
在我看來,在遞迴中遞迴的退出條件就是剪枝操作
**

遞迴理解

**

漫談遞迴大神之筆

對於遞迴,最好的理解方式便是從函式的功能意義的層面來理解。瞭解一個問題如何被分解為它的子問題,這樣對於遞迴函式程式碼也就理解了。這裡有一個誤區(我也曾深陷其中),就是通過分析堆疊,分析一個一個函式的呼叫過程、輸出結果來分析遞迴的演算法。這是十分要不得的,這樣只會把自己弄暈,其實遞迴本質上也是函式的呼叫,呼叫的函式是自己或者不是自己其實沒什麼區別。在函式呼叫時總會把一些臨時資訊儲存到堆疊,堆疊只是為了函式能正確的返回,僅此而已。我們只要知道遞迴會導致大量的函式呼叫,大量的堆疊操作就可以了。

遞迴的框架
對於遞迴,用函式的功能來理解遞迴,這樣才能夠保證不出錯。

DFS( , )//名稱是DFS(),但並不僅僅適用於子問題
{
    //最開始的是遞迴在簡單情境下的退出,這一部分必須有,是遞迴的重要組成部分
    //可以理解為剪枝
    if(..)
        return;
    if(...)
    {
        ...
        ...
        return;
    }

    //下一部分就是不滿足簡單情形的情況
    //說明此時還不是簡單情形,那麼就要繼續將改問題減少成更小的子問題
    .....
    DFS()//化成更小的子問題
..... } int main() { DFS(... , ...); }

本題程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

int n;
int prime[40]={0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0};
//prime[i]表示i是否為素數
int a[30];
int visited[30];
int flag=0;
void DFS(int lest)
{
    if(lest>n)//第一個退出條件
        return;

    if(lest==n)//第二個退出條件
    {
        if(prime[a[n]+1]==1)
        {
            flag=1;
            for(int i=1;i<=n;i++)
                printf("%d ",a[i]);
            printf("\n");
        }

    }
    //以上兩個條件都是剪枝的內容

    for(int i=1;i<=n;i++)
    {
        if(visited[i]==0 && prime[a[lest]+i]==1)
        {
            a[lest+1]=i;
            visited[i]=1; 
            DFS(lest+1);
            visited[i]=0;

        }

    }

}
int main()
{
    int i=0;
    while(scanf("%d",&n)!=EOF && n!=0)
    {

        memset(visited,0,sizeof(visited));
        memset(a,0,sizeof(a));
        i++;
        flag=0;
        printf("Case %d:\n",i);
        a[1]=1;
        visited[1]=1;
        if(n==1)//特殊情況
        {
            printf("%d\n",1);
            continue;
        }
        if(n%2==0)//奇數不可能成立,也是剪枝內容
            DFS(1);

        if(flag==0)
            printf("No Answer\n");
    }
    return 0;
}