Codeforces 45G Prime Problem

题意

https://codeforces.com/problemset/problem/45/G
作为数论题,这个题面给出的问题情景可以打高分,所以把题目简述下
n n n个房子,编号 1.. n 1..n 1..n,现要给它们涂色,要求是:

  • 一幢房子涂一种颜色,涂同种颜色的房子编号之和应该是素数
  • 颜色种数不能太多,要尽量少
  • 不同颜色的房子不能连续在一起

输入样例

8

输出样例

1 2 2 1 1 1 1 2 //涂色1的房子编号之和是1+4+5+6+7=23,涂色2的房子编号之和是2+3+8=13,都是素数
或者
1 1 1 1 2 1 1 1

算法:构造、数论

题中在条件2的限制性,可以忽略条件3,因为若有很多种颜色的房子,肯定不符合条件2的限制。

由条件出发很难想到用什么数论算法来解决,但是研究样例可以发现这个“哥德巴赫猜想”——任意一个偶数可以拆成两个素数之和有关,只不过题目已知的数不一定是偶数,因此需要分类讨论:

假设 s = n ( n + 1 ) 2 s = \dfrac{n(n+1)}{2} s=2n(n+1),那么:

  • s s s是素数,那么所有房子直接涂1色
  • s s s不是素数,那么它有可能是奇数或偶数:
    • 如果 s s s是偶数,那么只要2种颜色即可:用歌德巴赫猜想枚举第一个素数,然后那所房子涂2,其余都涂1
    • 如果 s s s是奇数,那么 s − 2 s-2 s2可能是素数,若是也只要2种颜色,除2号房子是2外,其余都涂1
    • s − 2 s-2 s2可能也不是素数,那么至少需要3种颜色,此时可以有两种算法:
      • 算法1:直接将3号房子涂3,然后 s − 3 s-3 s3是偶数,按照哥德巴赫方式处理
      • 算法2:对于 s − 2 s-2 s2这个值,从大到小枚举小于 s − 3 s-3 s3的素数,这个素数对应的房子可以涂1,而余下部分肯定是偶数,按哥德巴赫方式处理

以下按算法1实现。对于求素数,暴力 n n n\sqrt{n} nn 、埃氏筛、线性筛都可以。实际素数判定次数很少,最大值 600 0 2 6000^2 60002用暴力应该也可以过。

#include
#include
#include
#include
using namespace std;

const int maxn = 6002 * 3000;
int prime[maxn], flag[maxn], a[6002], cnt = 0;

void get_prime()
{
   for (int i = 2; i < maxn; i++)
   {
       if (!flag[i])
       {
           prime[cnt++] = i;
           //flag[i] = true;
       }
       for (int j = 0; j < cnt && i * prime[j] < maxn; j++)
       {
           flag[prime[j] * i] = true;
           if (i % prime[j] == 0)
               break;
       }
   }
}

void goldbach(int s)
{
   for (int i = 2; (i << 1) <= s; i++)  // 严格讲,这里应该循环到(i <= s / 2 )
   {
       if (!flag[i] && !flag[s-i])
       {
           a[i] = 2;
           return;
       }
   }
}

int main()
{
   freopen("demo.in", "r", stdin);
   freopen("demo.out", "w", stdout); 

   int n, s;
   scanf("%d", &n);
   for (int i = 1; i <= n; i++)
       a[i] = 1;
   s = (n * (n+1)) >> 1;
   get_prime();

   if (flag[s])
   {
       if (s % 2 == 0)
       {
           // 偶数,2组
           goldbach(s);
       } else if (!flag[s-2]) {
           // s-2是素数
           a[2] = 2;
       } else {
           // s是奇数,s-2也不是素数,那么至少3组
           a[3] = 3;
           goldbach(s-3);
       }
   }

   for (int i = 1; i <= n; i++)
       printf("%d%s", a[i], i == n ? "\n" : " ");
   
   return 0;
}

你可能感兴趣的:(Algorithms,C-C++,算法,素数)