您的位置:首页 > 编程语言 > C语言/C++

HFUTOJ problem 1343实现总结

2017-04-08 17:48 190 查看

题目

First Blood

Time Limit: 1000 MS Memory Limit: 65536 KB

Total Submissions: 799 Accepted: 101

Description

盖伦是个小学一年级的学生,在一次数学课的时候,老师给他们出了一个难题:

老师给了一个正整数 n,需要在不大于n的范围内选择三个正整数(可以是相同的),使它们三个的最小公倍数尽可能的大。盖伦很想第一个解决这个问题,你能帮助盖伦拿到“first blood”吗?

Input

首先是一个正整数T,表示有T组测试数据

每组测试数据是一个正整数n(1<=n<=10^6)

Output

对于每组测试数据,输出最大的最小公倍数,每个输出单独占一行

Sample Input

2

9

7

Sample Output

504

210

Source

安徽省2015年“京胜杯”大学生程序设计竞赛

思路

刚开始想的时候是这样的:

- 如果输入n是个奇数,那么结果一定是n*(n-1)*(n-2),因为n与n-1一定互质,n-1与n-2一定互质,那么只要n与n-2互质,这样算出来结果就一定是最大的,而n是奇数,所以n与n-2的公因子gcd必然不含2,之后如果gcd再变大也不可能是n和n-2的公因子,因为n-(n-2)一定大于gcd了。

- 如果n是偶数,那么n与n-2就有公因子2,n与n-2不互质,所以无法取得最大公倍数,那么这里就取n,n-1,n-3就可以了。

- n小于2的时候要单独算结果,因为按上面的想法计算的话会有0结果。

结果是这样的

#include <iostream>
using namespace std;
int main(void)
{
int TestTimes = 0;
cin >> TestTimes;
while (TestTimes--)
{
int INPUT = 0;
cin >> INPUT;
if (INPUT == 1)
cout << "1" << endl;
else if (INPUT == 2)
cout << "2" << endl;
else if (INPUT == 3)
cout << "6" << endl;
else if (INPUT % 2)
cout << INPUT * (INPUT - 1) * (INPUT - 2) << endl;
else
cout << INPUT * (INPUT - 1) * (INPUT - 3) << endl;
}
return 0;
}


保(lan)险(de)起(suan)见,我还把n=3的情况也顺便单独拉出来了。

结果错了。。。。。。

因为没有考虑在n为偶数的情况下n与n-3会不会是3的倍数,而换个角度考虑,如果验证的话那么就会陷入n-4、n-5…的死循环中。因此为了解决第三个数究竟应该是多少,我写了个验证互质的函数,然后从n-2开始向下遍历,直到找到与n(n-1)互质的数为止。同时,由于担心这种算法会超时,我在开始遍历之前把num预先变成奇数,然后num-=2遍历,这样时间就可以直接减半。

结果是这样的

#include <iostream>
#include <algorithm>
using namespace std;
bool cheak(int a, int b) {                    // a > b
if (b == 1) return true;
/*int num = a % b;
if (!num) return false;
if (num == 1) return true;
cheak(max(b, num), min(b, num));*/
while (b != 0) {
int temp = b;
b = a%b;
a = temp;
}
if (a == 1)    return true;
else        return false;
}

int main(void){
int TestTimes = 0;
cin >> TestTimes;
while (TestTimes--){
int INPUT = 0;
cin >> INPUT;
if (INPUT == 1)            cout << "1" << endl;
else if (INPUT == 2)    cout << "2" << endl;
else {
int num = INPUT - 1;
if (!(num % 2) && cheak(INPUT*(INPUT - 1), --num)) {
cout << INPUT * (INPUT - 1) * num << endl;
continue;
}
while (num -= 2)
if (cheak(INPUT*(INPUT-1), num)/* && cheak(INPUT - 1, num)*/)
break;
cout << INPUT * (INPUT - 1) * num << endl;
}
}
return 0;
}


虽然做了时间优化,但是还是超时了(这时间尼玛扣得真死 = =)。

没办法,只能去网上找了解答。在看过一个超级高级的算法后我不禁陷入了懵逼,因为完全看不懂

长这样的:

算法描述

任意正整数v与v-1必定互质,v*(v-1)为两个数最小公倍数的最大值,v由最大范围n选起;

再选一数u,由于两数的最小公倍数与最大公约数之积等于两数之积,因此最小公倍数表示为(v*(v-1)u)/gcd(v(v-1),u),故在V选定的情况下最小公倍数的大小由u/gcd(v*(v-1),u)决定,因此u不能与v或v-1相同,u由v-2开始选取,直到v(v-1)*u < max_lcm为止(因为最小公倍数的最大值为三个数的乘积),记录这期间的最大的最小公倍数值max_lcm;

v值减一,若max_lcm < v*(v-1)(v-2),则u由v-2开始再进行一次第2步,完成后max_lcm 与 v(v-1)*(v-2)比较,直到不满足“<”,输出max_lcm。

注意事项

注意特殊值

n=1时,v-1,v-2不存在,max_lcm=1;

n=2时,v-2不存在,max_lcm=2;

shenmegui!!!(╯‵□′)╯︵┻━┻

完全看不懂啊!!!(╯‵□′)╯︵┻━┻

既然看不懂自然也没法实现了。。。起码我没法用这个算法自己写个程序出来,哪怕是用背的都不行。。。。

好几天之后,我终于在网上找到了个神奇的算法:

思路:

记住n是奇数结果就是 n*(n-1)*(n-2);

n时偶数且不是3的倍数 n*(n-1)(n-3); 是3的倍数 (n-1)(n-2)*(n-3)

用long long保存

???

嗯,我也不能理解,原因在于这算法乍看起来和我的没啥区别,但是能过。。。。区别在于,在我上面碰到无限验证的循环之后。。。他没有用n*(n-1)?这样的算法了,而是用了(n-1)(n-2)*(n-3)。。。

??????

我不能理解啊,问了别人这是为啥,结果得到的答案是“这是数学常识,记住就好了”。


b64a
???????

这数学常识颠覆了我20年的世界观啊。。。。。从来没听说过这玩意啊。。。。

虽然我最后还是靠这个算法过了,但是我至今仍未能理解。。。

我可能需要个高数老师来帮帮我。。。

结果

#include<iostream>
using namespace std;
int main()
{
int TestTime = 0;
long long num = 0;
cin >> TestTime;
while(TestTime--){
cin >> num;
if (num <= 2)   cout << num << endl;
else if (num%2) cout << num*(num-1)*(num-2) << endl;
else if (num%3) cout << num*(num-1)*(num-3) << endl;
else        cout << (num-1)*(num-2)*(num-3) << endl;
}
return 0;
}


p.s 不知道为啥,只要我把num的定义放在循环体里面,就会有结果错误,又是个我不能理解的迷。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 C++ HFUTOJ