【ACM】算法题-寻找ABCDEF (优化)(搜索)

寻找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(n5log(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.越是简单的东西,背后说不定越繁冗,但越繁冗的东西就需要细心,否则卡在一点一直出不来的感觉就很累哇,所以不断提醒自己把细节给程序,把理智给自己。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值