题目描述
算法思路分析:
基本知识:素数除了2以外全部都是奇数,而要两个数之和为奇数的话,这两个数必须要满足一奇一偶的原则
递归思路:由于该素数环要求由1开头,所以相当于我们只用将2~n这些数摆放在合适的位置使它形成素数环即可,用数组来存放这n个数,其物理存储上的位置相邻代表了其逻辑位置上的相邻,数组中下标为0的位置处存储的元素是固定的就为1,而数组中下标为1~n-1位置处需要放置2~n这n-1个数使它成为素数环,那么递归思路就是先安置好数组中下标为1的位置处应该放置哪个数,再安置下标为2位置处应该放置哪个数,当数组下标超过n-1时说明所有元素均全部安置完毕,此时就为程序的递归出口。
算法思路复现:若我们要判断下标为 i 的位置处是否可以放置某个元素,那么就需要将待放置的元素与下标为 i-1 位置处的元素进行加和看两者之和是否为素数,所以我们可以设计一个函数用于判断两个数加和是否为素数,若是则返回1若不是则返回0,如:
int is_sumprime(int a, int b)
{
int sum = a + b;
for (int i = 2; i < sum; i++)
{
if (sum % i == 0)
{
return 0;
}
}
return 1;
}
除此之外由于每一次我们都要遍历所有的数,看下标为i的位置处应该放置哪个数,所以有些数会重复遍历,要保证已经放置好了的数不被重复放置在其它的位置,那么我们就需要设计一个函数用于判断这个数是否放置过了,也就是看这个数是否在数组中已经存在,若已经存在那么就说明该数已经放置过了,就要跳过该数然后去放置下一个数,如:
int is_exists(int* array, int n, int o)
{
for (int i = 0; i < n; i++)
{
if (array[i] == o)
return 1;
}
return 0;
}
最后就是设计一个函数用于将这n个数摆放成素数环了,这个函数需要借助前面定义的两个函数的功能来辅助实现,将2~n这些数摆放在数组中1~n-1的位置处,这里有一个小技巧,由于下标为0的位置处已经摆放好元素1了,是一个奇数,所以现在要摆放下标为1位置处的元素,由于要两个数相加为奇数那么两个数必须是一奇一偶的组合,所以下标为1位置处的元素需要一个偶数,此时我们需要去遍历所有的偶数看哪个数可以被放置在下标为1的位置处,此时若某个偶数元素可以被放置在下标为1的位置上那么该元素应该满足和1之和是一个素数,且该元素没有被放置过,然后放置好下标为1位置处的元素之后,再去放置下标为2位置处的元素,放置的方法和放置下标为1位置处的元素的方法是一样的,即过程重复,那么只要递归调用本函数就可以。这里将要进行摆放的2~n-1这n-1个数先进行区分奇数和偶数,是对算法的一种改进,我的想法是由于每一次放置下标为i位置处的元素时都需要重复遍历所有数,看哪个数可以被放置在该位置处,所以如果我们事先知道在i位置处要放置的是奇数的话就可以只遍历所有的奇数,若我们知道位置i处要放置的是偶数的话就可以只遍历所有偶数,这样会使得效率高一点,而如何确定下标为i处的位置需要奇数还是偶数呢?由于素数环以1开头,且是一奇一偶的排列方式所以所有下标为奇数的位置处放置的都是偶数,所有下标为偶数的位置处放置的都是奇数,如:
void G_PrimeCircle(int n,int *odd_number,int *even_number,int on,int en,int tnum)
{
if (n >= tnum)
{
if (is_sumprime(pc[0], pc[tnum - 1]))
{
for (int i = 0; i < tnum; i++)
{
if (pc[i] == 0)
{
return;
}
}
for (int i = 0; i < tnum; i++)
{
printf("%d ", pc[i]);
}
printf("\n");
}
else
{
return;
}
}
//判断此位置需要奇数还是偶数
if (n % 2 != 0) //证明此位置需要偶数,下标为奇数的位置处需要放置偶数
{
for (int i = 0; i < on; i++)
{
if (is_sumprime(pc[n - 1], odd_number[i]) && !is_exists(pc, tnum, odd_number[i]))
{
pc[n] = odd_number[i];
G_PrimeCircle(n + 1, odd_number, even_number, on, en, tnum);
pc[n] = 0; //注意回溯回来之后要将上一种解法中存放在该位置处的数清零,这样才可以探寻下一种摆放方法
}
if (i == on - 1 && pc[n] == 0)
{
return;
}
}
}
else
{
for (int i = 0; i < en; i++) //此位置需要放置的是奇数
{
if (is_sumprime(pc[n - 1], even_number[i]) && !is_exists(pc, tnum, even_number[i]))
{
pc[n] = even_number[i];
G_PrimeCircle(n + 1, odd_number, even_number, on, en, tnum);
pc[n] = 0;
}
if (i == en - 1 && pc[n] == 0)
{
return;
}
}
}
}
完整程序源代码:
//素数环,两个数之和是素数的条件是这两个数要满足是一奇一偶,且这两个数之间是互质的,即这两个数的公因数只有1
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define N 16
int pc[N] = { 0 };
int count = 0;
//以下函数用于判断一个整数是否在一个整数数组中
int is_exists(int* array, int n, int o);
//如下函数用于判断两个数是否满足相加为素数的条件
int is_sumprime(int a, int b);
//如下函数用于将从1到n的数排列成素数环,方便起见每一个素数环都从1开始
void G_PrimeCircle(int n, int* odd_number, int* even_number, int on, int en,int tnum);
int main()
{
int n;
int odd_numbers[N] = { 0 };
int even_numbers[N] = { 0 };
int on;
int en;
pc[0] = 1;
while (scanf("%d", &n) != EOF)
{
count++;
printf("Case %d:\n", count);
on = en = 0;
for (int i = 2; i <= n; i++)
{
if (i % 2 == 0)
{
odd_numbers[on] = i;
on++;
}
else
{
even_numbers[en] = i;
en++;
}
}
for (int i = 1; i < N; i++)
{
pc[i] = 0;
}
G_PrimeCircle(1, odd_numbers, even_numbers, on, en, n);
printf("\n");
}
return 0;
}
int is_sumprime(int a, int b)
{
int sum = a + b;
for (int i = 2; i < sum; i++)
{
if (sum % i == 0)
{
return 0;
}
}
return 1;
}
int is_exists(int* array, int n, int o)
{
for (int i = 0; i < n; i++)
{
if (array[i] == o)
return 1;
}
return 0;
}
void G_PrimeCircle(int n,int *odd_number,int *even_number,int on,int en,int tnum)
{
if (n >= tnum)
{
if (is_sumprime(pc[0], pc[tnum - 1]))
{
for (int i = 0; i < tnum; i++)
{
if (pc[i] == 0)
{
return;
}
}
for (int i = 0; i < tnum; i++)
{
printf("%d ", pc[i]);
}
printf("\n");
}
else
{
return;
}
}
//判断此位置需要奇数还是偶数
if (n % 2 != 0) //证明此位置需要偶数,下标为奇数的位置处需要放置偶数
{
for (int i = 0; i < on; i++)
{
if (is_sumprime(pc[n - 1], odd_number[i]) && !is_exists(pc, tnum, odd_number[i]))
{
pc[n] = odd_number[i];
G_PrimeCircle(n + 1, odd_number, even_number, on, en, tnum);
pc[n] = 0; //注意回溯回来之后要将上一种解法中存放在该位置处的数清零,这样才可以探寻下一种摆放方法
}
if (i == on - 1 && pc[n] == 0)
{
return;
}
}
}
else
{
for (int i = 0; i < en; i++) //此位置需要放置的是奇数
{
if (is_sumprime(pc[n - 1], even_number[i]) && !is_exists(pc, tnum, even_number[i]))
{
pc[n] = even_number[i];
G_PrimeCircle(n + 1, odd_number, even_number, on, en, tnum);
pc[n] = 0;
}
if (i == en - 1 && pc[n] == 0)
{
return;
}
}
}
}
运行结果截图
后记:对于读不懂的代码,可以自己调试一遍,然后观察调试过程中个变量的值的变化情况,这种方法可以帮助我们理解别人的算法代码