回溯法 素数环问题

问题:

输入正整数n,把整数1,2,3,4…..n组成一个环,使得相邻的两个整数之和均为素数。输出是,从整数1开始逆时针排列,n<=16;

样例输入:
6
样例输出:
1 4 3 2 5 6
1 6 5 2 3 4

(1)直接枚举

首先我们先不用回溯法来做,
使用直接枚举全部排列,然后得出解:

#include<iostream>
#include<algorithm>

using namespace std;

int A[100], isp[100], n;//isp是素数表,用于存放素数 

int is_prime(int x){
    for(int i = 2; i < x; i++){
        if(x % i == 0) return 0;
    }
    return 1;
}

void printPrime(){
    //生成第一个排列,顺序排列, A[1] = 1 A[2] = 2 
    for(int i = 1; i <= n; i++){
        A[i] = i;
    }
    do{
        bool ok = true;
        for(int i = 1; i < n; i++){
            int index = A[i]+A[i+1];
            if(!isp[index]){
                ok = false;
                break;
            }
        }
        //第一个和最后一个数的和 
        if(!isp[A[1] + A[n]]){
            ok = false;
        }
        if(ok){
            cout << "当前结果"; 
            for(int i = 1; i <= n; i++){
                cout << A[i] << " ";
            }
            cout << endl;
        }
    } while(next_permutation(A+2, A+n+1));//第一个一定为1不变,只更新第2个到最后1个的排列
}  

int main(){
    cin >> n;
    //记录1~n*2的值是否为素数 
    for(int i = 2; i <= n*2; i++){
        isp[i] = is_prime(i);
    }
    printPrime();
}

但是直接枚举的话,到n=12已经很慢了,n=16更是得不出结果

(2)使用回溯法

使用回溯法的解答树:
这里写图片描述

代码:

#include<iostream>
#include<algorithm>

using namespace std;

int A[100], isp[100], vis[100], n;//isp是素数表,用于存放素数 

int is_prime(int x){
    for(int i = 2; 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++){//第一个位置值为1,循环测试1和 2-6某个数的和是否为素数 
            /*isp[i+A[cur-1]]是剪枝函数,
              假如A[cur-1] = 1, i=3,此时之和不为素数,则把该支点剪掉,不往该子树递归
              相比于直接枚举,回溯法思想便体现在此 
            */
            if(!vis[i] && isp[i+A[cur-1]]){
                A[cur] = i;
                vis[i] = 1;//当前i的值已经放在了cur的位置,当其它层次的递归遍历到值i时,避免重复 
                dfs(cur+1);
                //要复位
                vis[i] = 0;
                A[cur] = 0; 
            } 
        }
    }
}

int main(){
    cin >> n;
    for(int i = 2; i <= n*2; i++){
        isp[i] = is_prime(i);
    }
    A[0] = 1;
    dfs(1); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值