素数幻方
素数幻方
Problem Description
试在指定区间[c,d]找出9个素数,构成一个3阶素数幻方,使得该方阵中3行、3列与两对角线上的3个数之和均相等。
输入区间c,d,输出基于该区间素数构建的所有3阶素数幻方的个数。
Input
Output
Sample Input
1 120
Sample Output
2
解释:
这是一个好玩的题,说实话,很好玩。我 参考 https://blog.csdn.net/u013514928/article/details/44903743?utm_source=blogxgwz5,之后自己优化了一下时间。很感谢这位大佬,我原本的想法是,从4个数中去循环查找的,结果发现,自己的数学模型存在一个巨大问题,无法保证答案的唯一性,对此,我是很懵的,很谢谢这个大佬的这篇博客,受益匪浅。
首先,我大致介绍一下我自己对于大佬的博客的理解。
我们需要3*3阶的素数,也就是需要9 + 1个未知数。按照大佬的数学模型。
n-x n+w n-y
n+z n n-z
n+y n-w n+x
其中3*n = S, x > y > 0;
对于这个模型,我们假设n, x, y 是已知的,那么
w = x + y
z = x - y
所以,在x, y, z, w 中 w是最大的,并且,当x = 2 *y的时候。z == y,所以, x == 2*y 这里要注意一下。不能出现。
在大佬的博客里面,有一个范围的思考计算,这里我们是用不上的,但是,可以考虑去学一下。好吧,很有必要去学一下,学完要记得给大佬点赞。 2 <= y <= n-3 y+2 <= x <= n-3;这个题目已经给定上界,下界了,所以。。。
最核心的是这个数学模型,想到了这个模型,问题就好解决很多了,剩下的就是优化。
1、给定的是[c, d]是两个自然数,其中个数为d - c + 1, 但是这两个中间的素数,绝对小于 d - c + 1. 于是乎,打表,一个保存当前数是不是素数的flag_prime, 另一个记录全是素数的number_prime。
2、我要找到[c, d]中间的素数是那些,我只要知道第一个大于等于C的素数,最后一个小于等于D的素数,可以遍历去找,这里我用的二分,加快时间。得到一个下标区间[tc, td]
3、大佬从s开始找,也就是说,从[3*d, 3*d]开始遍历,我这里从n开始找,并且n是素数的下标,我就可以保存每一次的更新都是素数,而不再是 +1 +1 + 1了,这样时间再快一点点。
4、用两个for循环,这个时候,我不再是用y去遍历,而是,直接遍历素数,找到n+y, 然后通过后n+y-n得到y,再判断n-y是不是素数,这样,就不再是+1 +1 +1这样的遍历了,时间再快一点
具体的看代码:
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 const int N = 20000; 5 6 int flag_prime[3*N]; 7 int sums_prime = 0; 8 int number_prime[N]; 9 10 void init(){ 11 flag_prime[0] = flag_prime[1] = 1; 12 for (int i = 2; i < N; i++){ 13 if (!flag_prime[i]){ 14 number_prime[sums_prime++] = i; 15 for (int j = 2; j*i < N; j++){ 16 flag_prime[i*j] = 1; 17 } 18 } 19 } 20 } 21 22 //3 查找最后一个小于key的元素 23 int findLastLess( int len, int key) 24 { 25 int left = 0; 26 int right = len - 1; 27 int mid; 28 29 while (left <= right) { 30 mid = (left + right) / 2; 31 if(key > number_prime[mid]) { //当大于的时候,就可以把左边更新一下 32 left = mid + 1; 33 }else{ 34 right = mid - 1; 35 } 36 } 37 return right; 38 } 39 40 //4 查找第一个大于key的元素 41 int findFirstGreater(int len, int key) 42 { 43 int left = 0; 44 int right = len - 1; 45 int mid; 46 47 while (left <= right) { 48 mid = (left + right) / 2; 49 if (key < number_prime[mid]) { // 当小于的时候,就可以把右边更新一下 50 right = mid - 1; 51 } 52 else { 53 left = mid + 1; 54 } 55 } 56 return left; 57 } 58 59 int func(int n, int c, int d){ 60 61 // n = number_prime[n]; 62 int s = number_prime[n]; 63 int sums = 0; 64 65 for (int i = n+1; i <= d-2; i++){ 66 int y = number_prime[i] - s; 67 if (flag_prime[s - y]) continue; 68 69 for (int j = i+1; j <= d-1; j++){ 70 int x = number_prime[j] - s; 71 if(flag_prime[s - x])continue; 72 73 int z = x - y; 74 int w = x + y; 75 if(x == 2 * y || s - w < number_prime[c] || s + w > number_prime[d]) continue; 76 77 if (!(flag_prime[s-z] + flag_prime[s+z] + flag_prime[s + w] + flag_prime[s - w] + flag_prime[s + x] + flag_prime[s - x] + flag_prime[s - y] + flag_prime[s + y]) ){ 78 // printf("%5d %5d %5d\n", s-x, s+w, s-y); 79 // printf("%5d %5d %5d\n", s+z, s, s-z); 80 // printf("%5d %5d %5d\n", s+y, s-w, s+x); 81 // cout << endl; 82 sums++; 83 } 84 } 85 } 86 return sums; 87 } 88 89 int main(){ 90 init(); 91 int c, d; 92 while (cin >> c >> d){ 93 int sums = 0; 94 int tc = findFirstGreater(sums_prime, c); 95 int td = findLastLess(sums_prime, d); 96 // printf("%d %d\n", tc, td); 97 for (int s = tc+3; s <= td-3; s++){ 98 sums += func(s, tc, td); 99 } 100 printf("%d\n", sums); 101 } 102 return 0; 103 104 }
时间还是很长,但是我想不到,还可以怎么优化了。还是感谢,
https://blog.csdn.net/u013514928/article/details/44903743?utm_source=blogxgwz5 这个博客,没有这个数学模型,我完全解不出来。