题目传送:错误 - TYWZOJ
题目描述
你有N个正整数,a1,a2……an。 你觉得这N个正整数的最大公因数太小了,于是你想通过删除一些数使得剩下的数的最大公因数变大。 你现在想知道,你最少需要删掉多少个数?
输入格式
第一行包含一个正整数N。
第二行包含N个正整数,表示a1,a2……an。
输出格式
一行一个正整数,表示最少要删掉的数的个数。
样例
input
4 6 9 15 30
output
2
样例解释
当前的最大公因数是3,删掉6和9之后剩下的数最大公因数是15。
首先搞清楚最大公因数是什么。
我们知道,一个合数可以用他的质因子的乘积表示,最大公因数是几个数共同的因数,即为几个数共有的质因子的乘积。例如9=3*3 15=3*5 9和15共同的质因子只有3,所以最大公因数为3.
按照这个思路,要使最大公因数乘某个质因子(变大),就需要删除没有这个质因子的数。
理清楚思路,然后就很简单了,建一个数组a记录所有质因子出现次数(注意出现次数是指拥有这个因子的数的个数,如分解4,2只能记录一次),然后遍历这个数组,寻找每个元素与n的差距,当a[i]==n时说明所有数字都拥有这个质因数即它是最大公因数的一部分,continue,记录a[i]!=n时的最小gap=n-a[i],答案即为gap.
记录质因数时,我的打法是打了一遍欧拉筛得到质数表一个一个判,质数表筛到sqrt(10^6)就够了但是我因为懒直接筛到了10^6勤奋的同学自己特判剪枝一下
代码如下:
#include <bits/stdc++.h>
using namespace std;
long long prime[1000005];
bool isprime[1000005];
void Euler_sieve(int n) {
memset(isprime, 1, sizeof(isprime));
for (int i = 2; i <= n; i++) {
if (isprime[i] == 1)
prime[++prime[0]] = i;
for (int j = 1; j <= prime[0] && i * prime[j] <= n; j++) {
isprime[i * prime[j]] = false;
if (i % prime[j] == 0)
break;
}
}
}
long long n, a[1000005], temp;
int main() {
freopen("gcd.in", "r", stdin);
freopen("gcd.out", "w", stdout);
scanf("%lld", &n);
Euler_sieve(100005);
for (long long i = 1; i <= n; i++) {
scanf("%lld", &temp);
long long cnt = 0;
while (++cnt) {
if (temp % prime[cnt] == 0) {
a[prime[cnt]]++;
temp = temp / prime[cnt];
}
if (prime[cnt] > temp )
break;
}
}
long long maxx = 0;
for (long long i = 1; i <= 1000005; i++) {
if (a[i] != n)
maxx = max(maxx, a[i]);
}
printf("%lld", n-maxx);
return 0;
}