题目如下
到x星球旅行的游客都被发给一个整数,作为游客编号。
x星的国王有个怪癖,他只喜欢数字3,5和7。
国王规定,游客的编号如果只含有因子:3,5,7,就可以获得一份奖品。
我们来看前10个幸运数字是:
3 5 7 9 15 21 25 27 35 45
因而第11个幸运数字是:49
小明领到了一个幸运数字 59084709587505,他去领奖的时候,人家要求他准确地说出这是第几个幸运数字,否则领不到奖品。
请你帮小明计算一下,59084709587505是第几个幸运数字。
以下选项错误的是?
A.
typedef long long LL; const LL Max = 59084709587505; int a[3] = {3, 5, 7}; void Find(LL Max) { set<LL> se; LL t = 1; while (1) { for (int i = 0; i < 3; ++i) { LL tt = t * a[i]; if (tt <= Max) se.insert(tt); } t = *se.upper_bound(t); if (t == Max) break; } cout << se.size() << endl; } int main(void) { Find(Max); return 0; }
B.
int main() { set<long long> st; priority_queue<long long, vector<long long>, greater<long long>> pq; const int ok[3] = {3, 5, 7}; st.insert(1); pq.push(1); int times = 0; while (true) { long long lucky = pq.top(); pq.pop(); if (lucky == 59084709587505) { cout << times << endl; return 0; } times++; for (int i = 0; i < 3; i++) { long long b = lucky * ok[i]; if (!st.count(b)) { st.insert(b); pq.push(b); } } } return 0; }
C.
#define LL long long LL maxs = 59084709587505; set<LL> q; int main() { q.insert(3); q.insert(5); q.insert(7); set<LL>::iterator it; it = q.begin(); LL mid; while (*it <= maxs) { mid = *it; q.insert(mid * 3); q.insert(mid * 5); q.insert(mid * 7); it++; } int num = 0; for (it = q.begin(); it != q.end(); it++) { if (*it <= maxs) num++; } cout << num; return 0; }
D.
typedef long long LL; const LL MAX = 59084709587505; int main() { int a[3] = {3, 5, 7}; LL tou = 1; set<LL> s; while (true) { for (int i = 0; i < 3; i++) { LL tt = tou * a[i]; if (tt <= MAX) s.insert(tt); } tou = s.upper_bound(tou); if (tou >= MAX) break; } cout << s.size() << endl; return 0; }
题目分析
不知道为什么,最近几道题都是鸡蛋里面挑骨头,可能让我们自己写一种解法不是难事,但要在众多的解法中选出错误的大难就很难了,所以这也是我们从小考试不喜欢选错误答案的一个原因吧。ps:错误答案意味着要对四个答案分别进行计算,这运算量瞬间翻了4倍。
和上道题一样,这里不对四个选项进行对错判断,留给看题目的同学们。
从题目可知,小明的幸运数字为59084709587505,所谓幸运数字是公约数因子只包含3,5,7的数字,那么显然,3,5,7就是前三个幸运数,因为数字中因子只包含3,5,7那么我们是不是可以用3,5,7分别对3,5,7进行乘法运算?
oh!我真是个小机灵鬼,这样题目不久迎刃而解了吗?每次运算后用一个index表示第几个数,进行++运算。
那也就是说,对3,5,7进行乘法运算:
第一轮:
先用3乘以3,5,7,得到9,15,21;
再用5乘以3,5,7,得到15,25,35;
后用7乘以3,5,7,得到21,35,49;
第二轮:
先用3乘以9,15,21,得到27,45,63;
再用5乘以9,15,21,得到45,75,105;
后用7乘以9,15,21,得到63,105,147;
第三轮:
先用3乘以27,45,63,得到81,135,189;
再用5乘以27,45,63,得到135,225,315;
后用7乘以27,45,63,得到189,315,441;
......
我们发现里面会有很多重复的数字,是不是不太友好?
这时候我们注意到选项中提供了一个函数:upper_bound
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
你也可以直接在括号中写num,题目的选项中就是这么用的, t = *se.upper_bound(t);
这样就在se中得到了3,之后是5,之后是7,之后是9,之后是15,之后是21......,用这些数字分别乘以3,5,7
这样,每次只需用这些数字乘以上一次乘积之后得到的三位数即可。省事了不少。
所以在上面的举例中,应该这么写:
用3乘以3,5,7,得到9,15,21;
用5乘以3,5,7,得到15,25,35;
用7乘以3,5,7,得到21,35,49;
用9乘以3,5,7,得到27,45,63;
用15乘以3,5,7,得到45,75,105;
用21乘以3,5,7,得到63,105,147;
......
总结
所谓万变不离其宗,所有选项都脱离不了这个范畴,按照这个思路,我们不需要去看选项就可以写出正确的代码。对于这种选项的题目,太考验人了,对错也存在于须臾之间,不认真看还真容易被忽略呢。有问题,欢迎评论区留言讨论!