005题:
题意:
2520是最小的能够被1到10整除的数。
最小的能够被1到20整除的正数是多少?
思路:
我们能够想到,既然是12的倍数,那么也一定会是1,2,3,4,6的倍数;既然是14的倍数,也一定会是7的倍数;既然是15的倍数,也一定是5的倍数;既然是16的倍数,也一定是8的倍数;既然是18的倍数,也一定是9的倍数;既然是20的倍数,那一定也是10的倍数
所以,我们只需要考虑11~20的最小公倍数即可
思路一: 利用辗转相除法求11和12的最大公因子,进而通过11*12/gcd(11,12)
来求它们的最小公倍数,然后求最小公倍数和13的最小公倍数并更新最小公倍数,循环上述过程直到20
思路二:我们知道,两个数的最小公倍数,相比它俩相乘,缩小了它俩的公共因数倍。
类比到多个数,比如: 2,3, 6, 12,16:
2 = 2; 3 = 3; 6 = 3*2; 12 = 3 * 2* 2;16 = 2 * 2 * 2 * 2;
我们发现:
公因子2在2中存在1次,在6中存在1次,在12中存在2次,在16中存在4次
公因子3在3中存在1次,在6中存在1次,在12中存在1次
那么它们的最小公倍数,就是pow(2,4)* pow(3,1)= 48
也就是说,每个因子找这些数中它出现的最大次数,其它数都是重复的,可以去掉,然后累乘每个因子的最大次数幂即为最小公倍数。
除了运算速度快之外,还可以避免辗转相除法取余1e9+7导致错误的尴尬(只有累乘不会出错,但是中途取余求gcd会造成错误)
兴冲冲的发现了这个规律后,结果一百度发现已经存在这个定理了,叫短除法。。哎,优队算法就这样没了。。。
代码:
和之前一样,暴力求法不写
#include <stdio.h>
#include <algorithm>
#include <inttypes.h>
#define ll int64_t
#define d int32_t
using namespace std;
d a[10];
ll ans = 1; //最终结果
ll prime[8] = {2, 3, 5, 7, 11, 13, 17, 19}; //20以内的质数
//用于存储11~20
void init() {
for (d i = 0; i < 10; i ++) {
a[i] = 11 + i;
}
}
//快速幂函数
ll quick (ll a, d b) {
ll res = 1;
while (b) {
if (b & 1) res *= a;
b >>= 1;
a *= a;
}
return res;
}
//短除法
void short_division() {
for (d i = 0; i < 8; i ++) {
d maxx = 0; //对于11~20,能够循环整除prime[i]的最大次数
for (d j = 0; j < 10; j++) {
d inx = 0; //记录当前数能循环整除prime[i]的最大次数
while (a[j] % prime[i] == 0) {
a[j] /= prime[i];
inx ++;
}
maxx = max(maxx, inx);
}
ans *= quick(prime[i], maxx); //结果乘质因子的最大循环整除次数次幂
}
printf("%" PRId64"\n", ans); //最后结果为11~20,也就是1~20的最小公倍数
}
int main()
{
init();
short_division();
return 0;
}
最后结果为:232792560
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢