第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
蓝桥杯 宝石组合 https://www.lanqiao.cn/problems/19711/learning/
在一个神秘的森林里,住着一个小精灵名叫小蓝。有一天,他偶然发现了一个隐藏在树洞里的宝藏,里面装满了闪烁着美丽光芒的宝石。这些宝石都有着不同的颜色和形状,但最引人注目的是它们各自独特的 “闪亮度” 属性。每颗宝石都有一个与生俱来的特殊能力,可以发出不同强度的闪光。小蓝共找到了 n n n 枚宝石,第 i i i 枚宝石的 “闪亮度” 属性值为 H i H_i Hi,小蓝将会从这 n n n 枚宝石中选出三枚进行组合,组合之后的精美程度 S S S 可以用以下公式来衡量:
S = H a H b H c ⋅ LCM ( H a , H b , H c ) LCM ( H a , H b ) ⋅ LCM ( H a , H c ) LCM ( H b , H c ) S = H_a H_b H_c \cdot \frac{\operatorname{LCM}(H_a, H_b, H_c)}{\operatorname{LCM}(H_a, H_b) \cdot\operatorname{LCM}(H_a, H_c) \operatorname{LCM}(H_b, H_c)} S=HaHbHc⋅LCM(Ha,Hb)⋅LCM(Ha,Hc)LCM(Hb,Hc)LCM(Ha,Hb,Hc)
其中 LCM \operatorname{LCM} LCM 表示的是最小公倍数函数。
小蓝想要使得三枚宝石组合后的精美程度 S S S 尽可能的高,请你帮他找出精美程度最高的方案。如果存在多个方案 S S S 值相同,优先选择按照 H H H 值升序排列后字典序最小的方案。
第一行一个整数 n n n 表示宝石个数。
第二行有 n n n 个整数 H 1 , H 2 , … H n H_1, H_2, \dots H_n H1,H2,…Hn 表示每个宝石的闪亮度。
输出一行包含三个整数表示满足条件的三枚宝石的 “闪亮度”。
5
1 2 3 4 9
1 2 3
解决该问题的关键便是化简精美程度S的计算公式。
a , b a,b a,b 的最小公倍数 l c m ( a , b ) lcm(a,b) lcm(a,b)
a , b a,b a,b 的最大公因数 g c d ( a , b ) gcd(a,b) gcd(a,b)
a , b , c a,b,c a,b,c 的最小公倍数 l c m ( l c m ( a , b ) , c ) lcm(lcm(a,b),c) lcm(lcm(a,b),c) (二者先求最小公倍数,结果与第三个数求最小公倍数)
a , b , c a,b,c a,b,c 的最大公因数 g c d ( g c d ( a , b ) , c ) gcd(gcd(a,b),c) gcd(gcd(a,b),c) (二者先求最大公因数,结果与第三个数求最大公因数)
l c m ( a , b ) = a × b / g c d ( a , b ) lcm(a,b)=a\times b /gcd(a,b) lcm(a,b)=a×b/gcd(a,b)
g c d ( l c m ( a , b ) , c ) = l c m ( g c d ( a , c ) , g c d ( b , c ) ) gcd(lcm(a,b),c)=lcm(gcd(a,c),gcd(b,c)) gcd(lcm(a,b),c)=lcm(gcd(a,c),gcd(b,c)) (该公式推导详见最大公因数与最小公倍数关系)
(用a,b,c代替Ha,Hb,Hc)
S = a b c l c m ( a , b , c ) l c m ( a , b ) ⋅ l c m ( a , c ) ⋅ l c m ( b , c ) S=abc\frac{lcm(a,b,c)}{lcm(a,b)\cdot lcm(a,c)\cdot lcm(b,c)} S=abclcm(a,b)⋅lcm(a,c)⋅lcm(b,c)lcm(a,b,c)
= a b c l c m ( l c m ( a , b ) , c ) a b g c d ( a , b ) a c g c d ( a , c ) b c g c d ( b , c ) =abc\frac{lcm(lcm(a,b),c)}{\frac{ab}{gcd(a,b)}\frac{ac}{gcd(a,c)}\frac{bc}{gcd(b,c)}} =abcgcd(a,b)abgcd(a,c)acgcd(b,c)bclcm(lcm(a,b),c)
= a b c c l c m ( a , b ) g c d ( l c m ( a , b ) , c ) a b g c d ( a , b ) a c g c d ( a , c ) b c g c d ( b , c ) =abc\frac{\frac{clcm(a,b)}{gcd(lcm(a,b),c)}}{\frac{ab}{gcd(a,b)}\frac{ac}{gcd(a,c)}\frac{bc}{gcd(b,c)}} =abcgcd(a,b)abgcd(a,c)acgcd(b,c)bcgcd(lcm(a,b),c)clcm(a,b)
= c × l c m ( a , b ) g c d ( a , b ) g c d ( a , c ) g c d ( b , c ) a b c × g c d ( l c m ( a , b ) , c ) =\frac{c\times lcm(a,b)gcd(a,b)gcd(a,c)gcd(b,c)}{abc\times gcd(lcm(a,b),c)} =abc×gcd(lcm(a,b),c)c×lcm(a,b)gcd(a,b)gcd(a,c)gcd(b,c)
= l c m ( a , b ) g c d ( a , b ) g c d ( a , c ) g c d ( b , c ) a b × l c m ( g c d ( a , c ) , g c d ( b , c ) ) =\frac{lcm(a,b)gcd(a,b)gcd(a,c)gcd(b,c)}{ab\times lcm(gcd(a,c),gcd(b,c))} =ab×lcm(gcd(a,c),gcd(b,c))lcm(a,b)gcd(a,b)gcd(a,c)gcd(b,c)
= a b g c d ( a , b ) g c d ( a , b ) g c d ( a , c ) g c d ( b , c ) a b g c d ( a , c ) g c d ( b , c ) g c d ( g c d ( a , c ) , g c d ( b , c ) ) =\frac{\frac{ab}{gcd(a,b)}gcd(a,b)gcd(a,c)gcd(b,c)}{ab\frac{gcd(a,c)gcd(b,c)}{gcd(gcd(a,c),gcd(b,c))}} =abgcd(gcd(a,c),gcd(b,c))gcd(a,c)gcd(b,c)gcd(a,b)abgcd(a,b)gcd(a,c)gcd(b,c)
= g c d ( g c d ( a , c ) , g c d ( b , c ) ) =gcd(gcd(a,c),gcd(b,c)) =gcd(gcd(a,c),gcd(b,c))
= g c d ( a , b , c ) =gcd(a,b,c) =gcd(a,b,c)
即最终化简为 S = g c d ( a , b , c ) S=gcd(a,b,c) S=gcd(a,b,c),找出三个数使其的最大公因数最大。
预处理,用cnt[i] 数组存储 i 值出现的次数,用vector v[i]存储i的倍数,
从大到小遍历每个数,当遇到第一个v[i] 中存储的倍数的个数大于等于3时,此时的 i 就是最大公因数。
将v[i]进行排序,则前三个数便是最大公因数i最大的三个数。
#include
#include
#include
using namespace std;
const int N = 1e5 + 10; // 定义常量 N,表示闪亮度值的最大范围
int cnt[N]; // 定义数组 cnt,用于统计每个闪亮度值的出现次数
int n, m; // n 表示宝石的数量,m 表示最大的闪亮度值
vector<int> v[N]; // 定义向量数组 v,用于存储每个因数的闪亮度值
int main() {
// 输入宝石的数量 n
scanf("%d", &n);
// 输入每颗宝石的闪亮度值,并统计每个闪亮度值的出现次数
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
cnt[x]++; // 统计闪亮度值 x 的出现次数
m = max(m, x); // 更新最大闪亮度值 m
}
// 对于每个可能的因数 i,收集所有能被 i 整除的闪亮度值
for (int i = 1; i <= m; i++) {
for (int j = i; j <= m; j += i) {
if (cnt[j]) { // 如果闪亮度值 j 存在
for (int k = 0; k < cnt[j]; k++)
v[i].push_back(j); // 将 j 添加到因数 i 的向量中
}
}
}
// 从最大的因数开始检查,找到第一个满足至少有 3 个闪亮度值的因数 i
for (int i = m; i >= 1; i--) {
if (v[i].size() >= 3) { // 如果因数 i 的闪亮度值数量 >= 3
sort(v[i].begin(), v[i].end()); // 对闪亮度值进行升序排序
for (int j = 0; j < 3; j++) printf("%d ", v[i][j]); // 输出最小的 3 个闪亮度值
break; // 找到后立即退出循环
}
}
return 0;
}