素数幻方

素数幻方

 

Problem Description

素数幻方全是由素数构成的各行、各列与两对角线之和均相等的方阵。
试在指定区间[c,d]找出9个素数,构成一个3阶素数幻方,使得该方阵中3行、3列与两对角线上的3个数之和均相等。
输入区间c,d,输出基于该区间素数构建的所有3阶素数幻方的个数。

Input

每行2个整数,表示c和d。

Output

每行1个整数,表示[c,d]之间所有3阶素数幻方的个数。

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 }
View Code

 

时间还是很长,但是我想不到,还可以怎么优化了。还是感谢,

 https://blog.csdn.net/u013514928/article/details/44903743?utm_source=blogxgwz5  这个博客,没有这个数学模型,我完全解不出来。

posted @ 2019-07-18 15:30  龚政  阅读(936)  评论(0编辑  收藏  举报