题目 | 质数之谜
第九届中国大学生程序设计竞赛(秦皇岛)-(CCPC2023-Qinhuangdao)
F - 质数之谜
https://cpc.csgrandeur.cn/csgoj/problemset/problem?pid=1229
1 second / 256 megabytes
standard input / standard output
题目
数学王国的质数部长是质数的狂热追随者,因此他特别设立了一个部门来解决与质数相关的问题。这个部门的负责人罗伯特先生最近收到了他的朋友欧拉的一封信。
这封信中包含了一个关于质数的谜题。欧拉认为序列 $a_1, a_2, ..., a_n$ 是美丽的当且仅当所有元素都是正整数,并且任意两个相邻元素的和是一个质数。
形式化地说,$\forall i\in [1, n] \cap \mathbb{N}, a_i \in \mathbb{N}^+,\forall i\in [1, n) \cap \mathbb{N}, (a_i+a_{i+1}) \in \mathbb{P}$,其中 $\mathbb{P}$ 表示包含所有质数的集合。
有时,欧拉信中给定的序列并不美丽。罗伯特先生希望通过修改最少数量的序列元素使其变得美丽。
罗伯特先生最近很忙,所以他想请你帮忙计算使序列变得美丽的最小修改数量。
输入
第一行包含一个整数 $n\ (2\le n \le 10^5)$。
第二行包含 $n$ 个正整数,表示 $a_1, a_2,...,a_n\ (1\le a_i\le 10^5)$。
输出
输出一个整数,表示使序列变得美丽的最小更新元素数量。
样例
输入
6
1 5 1 4 4 1
输出
2
输入
9
30 6 7 12 15 8 20 17 14
输出
4
解释
对于第一个测试,更新后的序列可以是 “1 2 1 1 4 1”。
题解
孪生素数猜想 / 波利尼亚克猜想
首先介绍一下本题需要用到的重要猜想——孪生素数才行:存在无穷多个素数 $p$,使得 $p+2$ 是素数,素数对 $(p,p+2)$ 成为孪生素数。
波利尼亚克拓展了范围:对于所有自然数 $k$,存在无穷多个素数对 $(p,p+2k)$.
猜想应用
除了偶素数 $2$ 可以被 $1+1$ 凑出,其他情况下素数一定是奇数,那么一定由一奇一偶凑出。
另外,根据上面的猜想,如果 $a,?,b$ 中,若 $a,b$ 同奇偶性(即 $|a-b|=2k$),那么可以找到一种 $?$ 的填法,使左右都能凑出素数(即$|(a+?)-(b+?)|=2k$ 符合孪生质数猜想)。
因此,如果对于序列第 $i$ 位,如果第 $i-1$ 位修改为了除 $1$ 之外的数,那么第 $i$ 位是可以不用修改的,因为两端一定是同奇偶的。
动态规划
因为 $1+1=2$ 的特殊性,所以需要特别判断这种情况。
状态表示:$dp_{ij}:=$ 考虑 $1\sim i$ 区间,且第 $i$ 位为 $j$ 状态所需最小步数。
- $j=0$:第 $i$ 位不改
- $j=1$:第 $i$ 位改成 $1$
- $j=2$:第 $i$ 位改成偶数
- $j=3$:第 $i$ 位改成奇数(除 $1$)
状态计算:
- $0\to0$:当且仅当 $a_{i-1}+a_{i}$ 为素数时可转移
- $1\to0$:当且仅当 $1+a_{i}$ 为素数时可转移
- $2\to0$:当且仅当 $a_i$ 为奇数时可转移
- $3\to0$:当且仅当 $a_i$ 为偶数时可转移
- $0\to1$:当且仅当 $a_{i-1}+1$ 为素数时可转移
- $1\to1$:恒可转移($1+1=2$ 恒为素数)
- $2\to1$:恒可转移
- $3\to1$:不可转移(奇 + $1$ 不可能为素数)
- $0\to2$:当且仅当 $a_{i-1}$ 为奇数时可转移
- $1\to2$:恒可转移
- $2\to2$:不可转移(偶 + 偶 不可能为素数)
- $3\to2$:恒可转移
- $0\to3$:当且仅当 $a_{i-1}$ 为偶数时可转移
- $1\to3$:不可转移($1$ + 奇 不可能为素数)
- $2\to3$:恒可转移
- $3\to3$:不可转移(奇 + 奇 不可能为素数)
通过上面的转移方式,转移时取最小值即可。对于初值,$dp_{10}=0$,$dp_{11}=dp_{12}=dp_{13}=1$.
时间复杂度为:$O(n)$
转移后,最终答案为:$\min\{dp_{n0},dp_{n1},dp_{n2},dp_{n3}\}$
代码
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
constexpr int MAXN = 2e5 + 10;
int prime[MAXN], idx;
bool not_prime[MAXN];
int n;
int a[MAXN];
int dp[MAXN][4];
void init_prime()
{
not_prime[0] = not_prime[1] = true;
for (int i = 2; i < MAXN; i++)
{
if (!not_prime[i])
prime[idx++] = i;
for (int j = 0; j < idx && i * prime[j] < MAXN; j++)
{
not_prime[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
memset(dp, 0x3f, sizeof(dp));
dp[1][0] = 0;
dp[1][1] = dp[1][2] = dp[1][3] = 1;
for (int i = 2; i <= n; i++)
{
// 0 -> 0
if (!not_prime[a[i - 1] + a[i]])
dp[i][0] = min(dp[i][0], dp[i - 1][0]);
// 1 -> 0
if (!not_prime[1 + a[i]])
dp[i][0] = min(dp[i][0], dp[i - 1][1]);
// 2 -> 0
if (a[i] % 2)
dp[i][0] = min(dp[i][0], dp[i - 1][2]);
// 3 -> 0
else
dp[i][0] = min(dp[i][0], dp[i - 1][3]);
///////////////////////////////////////////////
// 0 -> 1
if (!not_prime[a[i - 1] + 1])
dp[i][1] = min(dp[i][1], dp[i - 1][0] + 1);
// 1 -> 1
dp[i][1] = min(dp[i][1], dp[i - 1][1] + 1);
// 2 -> 1
dp[i][1] = min(dp[i][1], dp[i - 1][2] + 1);
// 3 -> 1: illegal
///////////////////////////////////////////////
// 0 -> 2
if (a[i - 1] % 2)
dp[i][2] = min(dp[i][2], dp[i - 1][0] + 1);
// 1 -> 2
dp[i][2] = min(dp[i][2], dp[i - 1][1] + 1);
// 2 -> 2: illegal
// 3 -> 2
dp[i][2] = min(dp[i][2], dp[i - 1][3] + 1);
///////////////////////////////////////////////
// 0 -> 3
if (a[i - 1] % 2 == 0)
dp[i][3] = min(dp[i][3], dp[i - 1][0] + 1);
// 1 -> 3: illegal
// 2 -> 3
dp[i][3] = min(dp[i][3], dp[i - 1][2] + 1);
// 3 -> 3: illegal
}
cout << min({dp[n][0], dp[n][1], dp[n][2], dp[n][3]}) << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init_prime();
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi