Codeforces 45G Prime Problem

題意

https://codeforces.com/problemset/problem/45/G
作爲數論題,這個題面給出的問題情景可以打高分,所以把題目簡述下
nn個房子,編號1..n1..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)2s = \dfrac{n(n+1)}{2},那麼:

  • ss是素數,那麼所有房子直接塗1色
  • ss不是素數,那麼它有可能是奇數或偶數:
    • 如果ss是偶數,那麼只要2種顏色即可:用歌德巴赫猜想枚舉第一個素數,然後那所房子塗2,其餘都塗1
    • 如果ss是奇數,那麼s2s-2可能是素數,若是也只要2種顏色,除2號房子是2外,其餘都塗1
    • s2s-2可能也不是素數,那麼至少需要3種顏色,此時可以有兩種算法:
      • 算法1:直接將3號房子塗3,然後s3s-3是偶數,按照哥德巴赫方式處理
      • 算法2:對於s2s-2這個值,從大到小枚舉小於s3s-3的素數,這個素數對應的房子可以塗1,而餘下部分肯定是偶數,按哥德巴赫方式處理

以下按算法1實現。對於求素數,暴力nnn\sqrt{n}、埃氏篩、線性篩都可以。實際素數判定次數很少,最大值600026000^2用暴力應該也可以過。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章