本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。
欢迎大家订阅我的专栏:算法题解:C++与Python实现!
附上汇总贴:算法竞赛备考冲刺必刷题(C++) | 汇总
【题目来源】
洛谷:P1214 [USACO1.4] 等差数列 Arithmetic Progressions
【题目描述】
一个等差数列是一个能表示成 a , a + b , a + 2 b , … , a + n b ( n ∈ N ) a, a+b, a+2b, \dots ,a+nb\space (n \in \mathbb N) a,a+b,a+2b,…,a+nb (n∈N) 的数列。
在这个问题中
a
a
a 是一个非负的整数,
b
b
b 是正整数。
写一个程序来找出在双平方数集合:
{
x
∣
x
=
p
2
+
q
2
∧
p
,
q
∈
N
∩
[
0
,
m
]
}
\{ x | x = p^2 + q^2 \wedge p,q \in \mathbb N \cap [0,m]\}
{x∣x=p2+q2∧p,q∈N∩[0,m]}
中长度为
n
n
n 的等差数列。
【输入】
第一行一个正整数
n
n
n,表示要找的数列长度。
第二行一个非负整数
m
m
m,表示
p
,
q
p,q
p,q 的上界。
【输出】
如果没有找到数列,输出 NONE。
如果找到了,输出一行或多行,每行由二个整数组成: a , b a,b a,b。
这些行应该以 b b b 为第一关键字, a a a 为第二关键字升序排序。
所求的等差数列将不会多于 10 , 000 10,000 10,000 个。
【输入样例】
5
7
【输出样例】
1 4
37 4
2 8
29 8
1 12
5 12
13 12
17 12
5 20
2 24
【核心思想】
-
问题分析:给定 n n n 和 m m m,在双平方数集合 S = { p 2 + q 2 ∣ 0 ≤ p , q ≤ m } S = \{p^2 + q^2 \mid 0 \le p, q \le m\} S={p2+q2∣0≤p,q≤m} 中找出所有长度为 n n n 的等差数列 a , a + b , a + 2 b , … , a + ( n − 1 ) b a, a+b, a+2b, \ldots, a+(n-1)b a,a+b,a+2b,…,a+(n−1)b。要求输出按 b b b 为第一关键字、 a a a 为第二关键字升序排列。
-
算法选择:
- 筛法标记双平方数:用布尔数组 f f f 标记所有 p 2 + q 2 p^2 + q^2 p2+q2( 0 ≤ p , q ≤ m 0 \le p, q \le m 0≤p,q≤m)
- 提取有序集合:将所有双平方数按升序存入数组 n u m num num
- 枚举公差和首项:外层枚举公差 b b b,中层枚举首项 a a a,内层验证连续 n n n 项是否均为双平方数
-
关键步骤:
- 读入: n n n(数列长度)、 m m m( p , q p, q p,q 上界)
- 生成双平方数:
- 双重循环枚举 p , q ∈ [ 0 , m ] p, q \in [0, m] p,q∈[0,m],标记 f [ p 2 + q 2 ] = 1 f[p^2 + q^2] = 1 f[p2+q2]=1
- 提取有序数组:
- 遍历
i
i
i 从
0
0
0 到
N
N
N,若
f
[
i
]
=
1
f[i] = 1
f[i]=1 则
num[++cur] = i
- 遍历
i
i
i 从
0
0
0 到
N
N
N,若
f
[
i
]
=
1
f[i] = 1
f[i]=1 则
- 枚举等差数列:
- 外层
b
b
b 从
1
1
1 到
num[cur](最大双平方数) - 中层
a
a
a 从
num[1]到num[cur-1]:- 剪枝:若
a
+
(
n
−
1
)
⋅
b
>
max_num
a + (n-1) \cdot b > \text{max\_num}
a+(n−1)⋅b>max_num,后续更大的
a
a
a 也不可能,直接
break - 内层验证 k k k 从 2 2 2 到 n n n:检查 a + ( k − 1 ) ⋅ b a + (k-1) \cdot b a+(k−1)⋅b 是否为双平方数
- 若全部
n
n
n 项均为双平方数:输出
a
a
a 和
b
b
b,标记
flag = true
- 剪枝:若
a
+
(
n
−
1
)
⋅
b
>
max_num
a + (n-1) \cdot b > \text{max\_num}
a+(n−1)⋅b>max_num,后续更大的
a
a
a 也不可能,直接
- 外层
b
b
b 从
1
1
1 到
- 输出:若
flag == false输出NONE
-
时间/空间复杂度:
- 时间复杂度: O ( m 2 + max_num ⋅ ∣ S ∣ ⋅ n ) O(m^2 + \text{max\_num} \cdot |S| \cdot n) O(m2+max_num⋅∣S∣⋅n),筛法 O ( m 2 ) O(m^2) O(m2),枚举验证部分取决于双平方数密度
- 空间复杂度: O ( N ) O(N) O(N), N = 2 m 2 + 5 N = 2m^2 + 5 N=2m2+5,布尔标记数组和存储数组
-
枚举验证的核心思想:
- 筛法预处理:将"是否为双平方数"的判定预处理为 O ( 1 ) O(1) O(1) 查询,避免每次计算平方和
- 有序集合枚举:提取有序数组后,按 b b b 和 a a a 的顺序自然满足输出排序要求
- 剪枝优化:利用 a + ( n − 1 ) b ≤ max_num a + (n-1)b \le \text{max\_num} a+(n−1)b≤max_num 提前终止不可能的首项枚举
- 验证而非构造:不直接构造等差数列,而是枚举参数后验证每项的 membership
- 适用于数论集合查询、等差数列枚举、筛法预处理类问题
【算法标签】
#普及- #数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 250 * 250 * 2 + 5; // 定义数组最大容量:m最大250,p²+q²最大为250²+250²=125000
int n, m; // n为要找的等差数列长度,m为p和q的上界
int f[N], cur, k; // f[i]标记i是否为双平方数;cur记录双平方数的个数;k用于循环计数
int num[N]; // num数组存储所有双平方数(有序)
bool flag; // flag标记是否找到至少一个等差数列
int main()
{
cin >> n >> m; // 读入数列长度n和p,q的上界m
// 第一步:生成所有双平方数(p²+q²,其中0≤p,q≤m)
for (int i = 0; i <= m; i++) // 枚举p
for (int j = 0; j <= m; j++) // 枚举q
f[i * i + j * j] = 1; // 标记p²+q²为双平方数
// 第二步:将所有双平方数按升序存入num数组
for (int i = 0; i < N; i++)
if (f[i]) // 如果i是双平方数
num[++cur] = i; // 加入num数组,cur计数加1
// 第三步:枚举所有可能的等差数列
// 外层循环枚举公差i(b)
for (int i = 1; i < num[cur]; i++) // 公差i从1到最大双平方数
{
// 中层循环枚举首项num[j](a)
for (int j = 1; j < cur - 1; j++) // 首项从第一个双平方数开始
{
// 剪枝:如果首项加上(n-1)倍公差超过最大双平方数,后续更大的首项也不可能,直接退出
if (num[j] + (n - 1) * i > num[cur])
break;
// 内层循环验证从num[j]开始、公差为i的n项是否都是双平方数
for (k = 2; k <= n; k++) // 验证第2项到第n项
if (!f[num[j] + (k - 1) * i]) // 如果某项不是双平方数
break; // 验证失败,退出内层循环
// 如果k达到n+1,说明所有n项都是双平方数,找到了一个合法等差数列
if (k == n + 1 && f[num[j] + (n - 1) * i])
{
cout << num[j] << " " << i << endl; // 输出首项a和公差b
flag = true; // 标记找到了等差数列
}
}
}
// 如果没有找到任何等差数列,输出NONE
if (!flag)
cout << "NONE" << endl;
return 0;
}
【运行结果】
5
7
1 4
37 4
2 8
29 8
1 12
5 12
13 12
17 12
5 20
2 24
159

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



