题意
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 s−2可能是素数,若是也只要2种颜色,除2号房子是2外,其余都涂1
-
s
−
2
s-2
s−2可能也不是素数,那么至少需要3种颜色,此时可以有两种算法:
- 算法1:直接将3号房子涂3,然后 s − 3 s-3 s−3是偶数,按照哥德巴赫方式处理
- 算法2:对于 s − 2 s-2 s−2这个值,从大到小枚举小于 s − 3 s-3 s−3的素数,这个素数对应的房子可以涂1,而余下部分肯定是偶数,按哥德巴赫方式处理
以下按算法1实现。对于求素数,暴力 n n n\sqrt{n} nn、埃氏筛、线性筛都可以。实际素数判定次数很少,最大值 600 0 2 6000^2 60002用暴力应该也可以过。
#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;
}

本文解析Codeforces上的一道数论题,通过构造和数论算法解决房屋涂色问题,确保同色房子编号之和为素数,同时颜色种类最少。详细介绍了基于哥德巴赫猜想的解决方案,并提供了C++实现代码。
486

被折叠的 条评论
为什么被折叠?



