题解:洛谷 P17011 晚宴
【题目来源】
洛谷:P17011 [GESP202606 五级] 晚宴 - 洛谷
【题目描述】
小明去参加晚宴。晚宴中有 \(n\) 个菜肴,每个菜肴都有一个美味度,第 \(i\) 个菜肴的美味度为 \(v_i\)。
晚宴规定小明只能恰好选取两道菜肴,并且这两道菜肴的美味度必须要互质(即最大公约数为 \(1\))。
请帮助小明选取两道菜肴,使得两道菜肴美味度之和最大。
【输入】
输入 \(2\) 行,
第一行为一个正整数 \(n\),表示菜肴的个数;
第二行为 \(n\) 个整数 \(v_1, v_2, \cdots, v_n\) 表示菜肴的美味度,整数之间以空格分隔。
【输出】
输出一个整数,表示两道互质菜肴美味度之和的最大值。
【输入样例】
5
3 5 7 35 105
【输出样例】
38
【核心思想】
-
问题分析:给定 \(n\) 个菜肴的美味度 \(v_1, v_2, \ldots, v_n\),要求恰好选取两道互质(\(\gcd(v_i, v_j) = 1\))的菜肴,使得美味度之和最大。这是一个枚举 + 最大公约数问题,核心在于遍历所有不重复的菜对并筛选满足互质条件的最大和。
-
算法选择:
- 双重枚举:枚举所有 \(C_n^2 = \frac{n(n-1)}{2}\) 种菜对组合
- 最大公约数判定:利用
__gcd函数判断两数是否互质
-
关键步骤:
- 读入数据:读取 \(n\) 和数组 \(a[1..n]\)
- 枚举菜对(\(i\) 从 \(1\) 到 \(n-1\),\(j\) 从 \(i+1\) 到 \(n\)):
- 计算 \(\gcd(a_i, a_j)\)
- 若 \(\gcd(a_i, a_j) = 1\),则 \(ans \leftarrow \max(ans, a_i + a_j)\)
- 输出结果:\(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2 \cdot \log V)\),其中 \(V\) 为美味度最大值,\(n^2\) 对枚举,每对 \(\gcd\) 计算为 \(O(\log V)\)
- 空间复杂度:\(O(1)\),仅使用常数额外空间
-
枚举与互质判定的核心思想:
- 全枚举保证最优:由于 \(n\) 规模较小(\(n \leq 1000\)),\(O(n^2)\) 的枚举量约为 \(5 \times 10^5\),在可接受范围内,直接遍历所有组合即可找到全局最优解
- 互质条件筛选:利用欧几里得算法(辗转相除法)计算最大公约数,当 \(\gcd = 1\) 时两数互质,满足题目约束
- 去重处理:通过 \(j\) 从 \(i+1\) 开始枚举,确保每对菜肴只被考虑一次,避免重复计算和自配对
- 贪心不可行:不能简单地选取两个最大数,因为它们可能不互质(如样例中的 \(35\) 和 \(105\) 的 \(\gcd = 35\)),必须验证互质条件
- 适用于约束规模较小、需要遍历所有组合并满足特定数学条件的枚举类问题
【算法标签】
普及- #欧几里得算法
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 1005; // 常量:最大菜肴数量
int n; // n: 菜肴个数
int ans; // ans: 两道互质菜肴美味度之和的最大值
int a[N]; // a[i]: 第 i 个菜肴的美味度
int main()
{
cin >> n; // 读入菜肴个数
for (int i = 1; i <= n; i++) // 读入 n 个菜肴的美味度
cin >> a[i];
for (int i = 1; i < n; i++) // 枚举第一道菜
for (int j = i + 1; j <= n; j++) // 枚举第二道菜(确保不重复选同一道菜)
if (__gcd(a[i], a[j]) == 1) // 如果两道菜的美味度互质
ans = max(ans, a[i] + a[j]); // 更新最大美味度之和
cout << ans << endl; // 输出最大美味度之和
return 0;
}
【运行结果】
5
3 5 7 35 105
38
【算法真传】
浙公网安备 33010602011771号