HDU1016--素数环问题

HDU1016–素数环问题

一、题目链接

HDU1016

二、题目内容

A ring is compose of n circles as shown in diagram. Put natural number 1, 2, …, n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.
Note: the number of first circle should always be 1.
在这里插入图片描述

Input

n (0 < n < 20).

Output

The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.
You are to write a program that completes above process.
Print a blank line after each case.

Sample Input

6
8

Sample Output

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

翻译

圆环由n个圆组成,如图所示。将自然数1、2,…,n分别放入每个圆,两个相邻圆中的数字总和应为质数。
注意:第一个圆的数目应始终为1。
输出格式如下所示。每一行代表环中从顺时针和逆时针1开始的一系列圆圈编号。数字顺序必须满足以上要求。按字典顺序打印解决方案。
您将编写一个完成上述过程的程序。
在每种情况下都打印空白行。

三、题意拆解

这次的题目非常的简洁明了。

将自然数1、2,…,n分别放入每个圆,两个相邻圆中的数字总和应为质数。
注意:圆的起始应始终为1。

这句话告诉我们三个信息:

  1. 从1到n的数要排成一个首尾相连的圆环,即第一个数和最后一个数字是相连的。
  2. 任意两个相邻的数字之和要是一个素数。
  3. 所有的输出数列要以1开头。

好,那么我们直接开始解题。

四、解题思路

首先,题目要求任意两个相邻的数字和是素数,并且要能组合成一个环用完所有的数。
那么我们第一反应是什么?依次把1后面的数字塞进去队列,看看他和前一个数能否构成一个素数,能的话就更新这串数列,不能的话就换到下一个数字塞进去。更新完数列后从头重新把数列塞进去,直到数列长度等于n或者有有数字塞不进去的情况。
让我们来看个例子。
比如我们输入

6

这时候我们先把队列初始化,此时队列为

1

然后我们看看2能不能塞到1后面,1+2=3是素数,可以塞进去,更新队列

1 2

然后看看3能不能塞进去,2+3=5是素数,塞进去,更新队列

1 2 3

然后看看4能不能塞进去,3+4=7是素数,塞进去,更新队列

1 2 3 4

依次类推,5+4=9不是素数,查看下一个
6+4=10不是素数,但此时已经没有下一个数可以去塞了,证明这个排序是不行的,那我们倒回上一个数字
此时队列回溯到

1 2 3

因为4会导致后续的数列无法完善,我们就继续看下一个数
3+5=8不是素数,下一个
3+6=9不是素数,但后面又没有数字了,我们继续回溯到上一个队列
此时队列回溯到

1 2

然后重复这样子的行为,我们能够找到第一条符合题意的队列

1 4 3 2 5 6

然后我们输出它,继续找它的下一个。
这个就是这一题的具体思路了。那么如何实现呢?
因为我们可能要回溯到上一步的队列,所以我们如何保存上一步的队列这很关键。学过数据结构的人应该清楚,在c++里面有这么一个概念- -栈。这刚好就是我们所需要的保存方式。
所以我们就用栈来保存,那么在数据结构里面什么方法就是用到栈呢?
没错就是递归,所以这一题我们用递归的思路来写。
解决了储存问题,接下来就是判断问题了。
因为我们每一次相加减我们就需要判断一次素数,因此我们最好是把判断素数写成一个函数,方便我们调用。
但是呢,调用函数也会占用大量运算时间,因此我们可以提前把素数给标记出来,也就是打一个素数表。
我们创建一个数组,因为题目n最大为20,所以我们创建个41的数组就够了,初始化为0,然后把不是素数的标记为1.具体打表我在代码里会有详细注释。
解决完这些大问题,接下来就是题目需要注意的细节了。
首先:题目是个多数据输入,并且每次数据输入都需要对应次序的标号
比如第一组数据我们要输出

Case 1:

第二个要注意的地方就是在每次数据找出所有可行性队列后,都要输出一行空白行。
第三点,也是本人一直忽视的点,在每列数据输出后不能有多余的空格出现。
以上就是这题的具体解法和要点了。

五、参考代码

//
// Created by Verber.
//
#include "iostream"
#include "bits/stdc++.h"
#include "queue"
using namespace std;
int n;
int biao[42],zu[22],pd[22];//三个数组分别储存的是素数表,数列,以及判断该数字是否入队
void sushudabiao()//打表函数
{
    memset(biao,0,sizeof(biao));//初始化数组
    for (int i = 2; i<= 42; i++)
    {
        if(biao[i]==0)//如果是素数,它的倍数一定不是素数
        {
            for(int t=i*2;t<=42;t+=i)
            {
                biao[t]=1;
            }
        }
    }

}
void zhaohuan(int num)//递归函数
{
    if (num == n && biao[zu[0] + zu[n - 1]]==0)//当递归层数等于n时并且首尾数相加是素数的情况下跳出递归,输出数列
    {
        for (int i = 0; i <n-1; i++)
        {
            cout<<zu[i]<<" ";
        }
        cout<<zu[n-1]<<endl;//最后一个数字后面不能有多余的空格
        return;
    }
    else
    {
        for (int j = 2; j <= n; j++)//循环去寻找下一个塞进去数列的数字
        {
            if (!pd[j] && biao[j + zu[num - 1]]==0)
            {
                zu[num] = j;//把该数字入列
                pd[j] = 1;//表示该数字已入列
                zhaohuan(num + 1);//如果该数字能塞到当前数列后面,重新查找下一个数字
                pd[j] = 0;//当执行完上一步的递归后,无论能否成数列,都要让该数字出列,查找新的能塞到当前位置的数字,找新的数列。
            }
        }
    }
}
int main()
{
    sushudabiao();
    int i=1;
    while (scanf("%d",&n)!=EOF) {
        cout<<"Case "<<i++<<":"<<endl;
        zu[0] = 1;
        memset(pd,0,sizeof(pd));//每一步初始化判断列表。可省略
        zhaohuan(1);
        cout<<endl;

    }
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页