寻找ABCDEF
题目描述
在[1,100]中寻找6个数ABCDEF,满足 A < = B < = C < = D < = E < = F A<=B<=C<=D<=E<=F A<=B<=C<=D<=E<=F A 5 + B 5 + C 5 + D 5 + E 5 = = F 5 A^5+B^5+C^5+D^5+E^5==F^5 A5+B5+C5+D5+E5==F5输出所有的解,无输入,输出ABCDEF用空格分割。
解析
题目条件很简单,重点依然是要程序在几秒内实现它,第一点很多人会想到的是六重循环直接遍历,设好条件满足后输出,比如这个函数,可是这个点子实在没有任何技巧可言,且无用: ( O ( n 6 ) ) (O(n^6)) (O(n6))
void step0() {
for (int a = 1; a <= N; a++)
for (int b = 1; b <= N; b++)
for (int c = 1; c <= N; c++)
for (int d = 1; d <= N; d++)
for (int e = 1; e <= N; e++)
for (int f = 1; f <= N; f++) {
if (h(a) + h(b) + h(c) + h(d) + h(e) == h(f)) {
cout << a << " " << b << " " << c << " " << e << " " << f << endl;
}
}
}
我们需要优化它,后面会有很快的算法来实现,可还是那句话,凡事需要过程,比起一蹴而就,点滴升阶更踏实,所以下面这个函数呢,至少提速了120倍,因为题目条件嘛:
void step1() {
for (int a = 1; a <= N; a++)
for (int b = a; b <= N; b++)
for (int c = b; c <= N; c++)
for (int d = c; d <= N; d++)
for (int e = d; e <= N; e++)
for (int f = e; f <= N; f++) {
if (h(a) + h(b) + h(c) + h(d) + h(e) == h(f)) {
cout << a << " " << b << " " << c << " " << e << " " << f << endl;
}
}
}
很显然,这样的速度也太慢了,我们不妨尝试空间换时间,用中括号代替圆括号,至少提速8倍,把每个数的五次方存到数组里面再用即可,如下:
//2.空间换时间
ll H[N + 1];
void Fill() {
for (int i = 0; i <= N; ++i) H[i] = h(i);
}//中括号提速八倍
void step2() {
Fill();
for (int a = 1; a <= N; a++)
for (int b = a; b <= N; b++)
for (int c = b; c <= N; c++)
for (int d = c; d <= N; d++)
for (int e = d; e <= N; e++)
for (int f = e; f <= N; f++) {
if (H[a] + H[b] + H[c] + H[d] + H[e] == H[f]) {
cout << a << " " << b << " " << c << " " << e << " " << f << endl;
}
}
}
即使如此,优化后的速度也是遥不可及的,我们在step2的基础上用二分查找代替循环是个不错的选择,其中二分查找函数需要写好比较器:
( O ( n 5 ∗ l o g ( n ) ) ) (O(n^5*log(n))) (O(n5∗log(n)))
ll H[N + 1];//3.空间换时间 用二分查找代替循环O(n^5 logN)
void Fill() {
for (int i = 0; i <= N; ++i) H[i] = h(i);
}
int cmp(const void *a, const void *b) {
ll *la = (ll *) a;
ll *lb = (ll *) b;
ll d = *la - *lb;
if (d < 0)return -1;
if (d > 0)return 1;
return 0;
}
void step3() {
Fill();
for (int a = 1; a <= N; a++)
for (int b = a; b <= N; b++)
for (int c = b; c <= N; c++)
for (int d = c; d <= N; d++)
for (int e = d; e <= N; e++) {
ll key = H[a] + H[b] + H[c] + H[d] + H[e];
ll *pos = (ll *) bsearch(&key, H, N - 1, sizeof(H[0]), cmp);
if (pos) {
cout << a << " " << b << " " << c << " " << e << " " << pos - H << endl;
}
}
}
在优化的路上不能止步,我们在step3的基础上对区间进行控制,减小范围来提高效率:
ll H[N + 1];
void Fill() {
for (int i = 0; i <= N; ++i) H[i] = h(i);
}
//4.空间换时间 用改进的二分查找代替循环区间控制
int cmp(const void *a, const void *b) {
ll *la = (ll *) a;
ll *lb = (ll *) b;
ll d = *la - *lb;
if (d < 0)return -1;
if (d > 0)return 1;
return 0;
}
void step4() {
Fill();
for (int a = 1; a <= N; a++)
for (int b = a; b <= N; b++)
for (int c = b; c <= N; c++)
for (int d = c; d <= N; d++)
for (int e = d; e <= N; e++) {
ll key = H[a] + H[b] + H[c] + H[d] + H[e];
ll *pos = (ll *) bsearch(&key, H + e + 1, N - e, sizeof(H[0]), cmp);
if (pos) {
cout << a << " " << b << " " << c << " " << e << " " << pos - H << endl;
}
}
}
到这里为止,我们就已经把程序优化很多了,调用函数后的话,时间也是在几秒左右,与最开始的idea运行速度相差太多,接下来提供一个非常巧妙的思路来step4的基础再上升华一下,那就是改进枚举法的顺序,这就略显奇怪与可爱了:
ll H[N + 1];
void Fill() {
for (int i = 0; i <= N; ++i) H[i] = h(i);
}
int cmp(const void *a, const void *b) {
ll *la = (ll *) a;
ll *lb = (ll *) b;
ll d = *la - *lb;
if (d < 0)return -1;
if (d > 0)return 1;
return 0;
}
//5.空间换时间 用改进的二分查找代替循环 改进枚举法顺序
void step5() {
Fill();
for (int e = 1; e <= N; e++)
for (int d = 1; d <= e; d++)
for (int c = 1; c <= d; c++)
for (int b = 1; b <= c; b++)
for (int a = 1; a <= b; a++) {
ll key = H[a] + H[b] + H[c] + H[d] + H[e];
ll *pos = (ll *) bsearch(&key, H + e + 1, N - e, sizeof(H[0]), cmp);
if (pos) {
cout << a << " " << b << " " << c << " " << e << " " << pos - H << endl;
}
}
}
那么综上用最优化后的代码流程有下:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll=long long;
const int N = 100;
ll h(int x) {
return 1ll * x * x * x * x * x;
}
ll H[N + 1];
void Fill() {
for (int i = 0; i <= N; ++i) H[i] = h(i);
}//中括号提速八倍
int cmp(const void *a, const void *b) {
ll *la = (ll *) a;
ll *lb = (ll *) b;
ll d = *la - *lb;
if (d < 0)return -1;
if (d > 0)return 1;
return 0;
}
//5.空间换时间 用改进的二分查找代替循环 改进枚举法顺序
void step5() {
Fill();
for (int e = 1; e <= N; e++)
for (int d = 1; d <= e; d++)
for (int c = 1; c <= d; c++)
for (int b = 1; b <= c; b++)
for (int a = 1; a <= b; a++) {
ll key = H[a] + H[b] + H[c] + H[d] + H[e];
ll *pos = (ll *) bsearch(&key, H + e + 1, N - e, sizeof(H[0]), cmp);
if (pos) {
cout << a << " " << b << " " << c << " " << d << " " << e << " " << pos - H << endl;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
step5();
return 0;
}```
## 总结:
1.first idea 很重要。
2.多用C++/C中的库与函数,确实省事不少。
3.越是简单的东西,背后说不定越繁冗,但越繁冗的东西就需要细心,否则卡在一点一直出不来的感觉就很累哇,所以不断提醒自己把细节给程序,把理智给自己。