【2024.10.8练习】宝石组合

题目描述


题目分析

由于是求最值,原本考虑贪心,但由于算式过于复杂,首先考虑对算式化简。

进行质因数分解:

H_a=p_1^{a_1}p_2^{a_2}...p_k^{a_k}

H_b=p_1^{b_1}p_2^{b_2}...p_k^{b_k}

H_c=p_1^{c_1}p_2^{c_2}...p_k^{c_k}

因此:

S=H_aH_bH_c\frac{LCM(H_aH_bH_c)}{LCM(H_aH_b)LCM(H_bH_c)LCM(H_aH_c)}

=\prod_{i=1}^{k} p_i^{a_i+b_i+c_i+max(a_i,b_i,c_i)-max(a_i,b_i)-max(b_i,c_i)-max(a_i,c_i)}

不妨设对于每个ia_i\geq b_i\geq c_i,则上式可化简为:

S=\prod_{i=1}^{k}p_i^{a_i+b_i+c_i+a_i-a_i-b_i-a_i}

=\prod_{i=1}^{k}p_i^{c_i}

S=GCD(H_a,H_b,H_c)

用Vene图也可以求出同样结果。

可是以现在的数据量,求任意两个数的最大公因数都会超时,显然不能直接遍历。故采用记忆化数组预处理数据的方式:设长度为max(Hi)数组cnt[i],表示的是i为多少颗宝石的因数,这样i即为这些宝石的一个公因数。而满足条件cnt[i]>=3i最大值,即为所有宝石任取三个的GCD。

	for(int i=1;i<=n;i++){
		cin>>h[i];
		for(int j=1;j<=h[i];j++) {
			if(h[i]%j==0){
				cnt[j]++;
			}
		}
	}

可这样复杂度仍为O(n^2)。联想到素数判定,如果d是n的约数,那么n/d也是n的约数。所以只需要检查1~√n的所有整数就足够了。这样复杂度就可以降为O(n\sqrt{n})


我的代码

注意在遍历中一旦求得gcd就使用break退出循环。

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX_N = 1e5 + 5;
int cnt[MAX_N]; 
int H[MAX_N];
int n;
int gcd;
int main() {
	//输入
	cin >> n;
	for (int i = 0; i < MAX_N; i++)
	{
		cnt[i] = 0;
	}
	for (int i = 0; i < n; i++)
	{
		cin >> H[i];
		//计数
		for (int j = 1; j * j <= H[i]; j++)
		{
			if (H[i] % j == 0) {
				cnt[j]++;
				if (j * j != H[i]) {
					cnt[H[i] / j]++;
				}
			}
		}
	}
	
	//确定最大公约数:
	for (int i = MAX_N-1; i >= 0; i--)
	{
		if (cnt[i] >= 3) {
			gcd = i;
			break;
		}
	}

	//排序
	sort(H, H + n);

	int ans = 0;
	for (int i = 0; i < n; i++)
	{
		if (H[i] % gcd == 0) {
			if (ans <= 2) {
				cout << H[i] << " ";
				ans++;
			}
			else if(ans==2){
				cout << H[i];
			}
		}
	}
	return 0;
}

思路扩展

为了不使用排序,还可以让cnt数组变成Vector向量容器数组vector<int> cnt[i];。若i为H[i]的因数,将H[i] push_back进Vector中,自然按字典序排列。最后按序输出cnt[gcd].at(0)、cnt[gcd].at(1)、cnt[gcd].at(2)即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值