第十届蓝桥杯C++ B组省赛解题思路

A 组队

最稳妥的方法是把数据copy出来写个搜索,当然也可以尝试直接在纸上找答案,有风险。

另外还有一种想法,因为一眼看上去这个答案就很接近500,先找到一组接近500的情况,然后类似找增广路一样看能否增大解。

B 年号字串

这不就是tmd26进制么,除26取余。

C 数列求值

写个递推,又快。除非你使用python,否则要对每项%10000.

D 数的分解

直接枚举。

E 迷宫

BFS。处理字典序仅需在建立方向数组时按照上右左下顺序即可。

F 特别数的和

好水

//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:TeBieShuDeHe.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//

#include 

using namespace std;

bool check(int x) {
     
    int tmp;
    while (x) {
     
        tmp = x % 10;
        if (tmp == 2 || tmp == 0 || tmp == 1 || tmp == 9) return true;
        x /= 10;
    }
    return false;
}

int main() {
     
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n;
    long long ans = 0;
    cin >> n;
    for (int i = 1; i <= n; i++)
        if (check(i)) ans += i;
    cout << ans << endl;
    return 0;
}

G 完全二叉树的权值

显然,第 k k k层的节点编号范围为 2 k − 1 ∼ 2 k − 1 2^{k-1}\sim 2^k-1 2k12k1,依次我们直接 O ( n ) O(n) O(n)求出每层权值和并打擂。

当然,也可以转换一下想法,每层 2 k − 1 2^{k-1} 2k1个节点,直接按每层节点读。

//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:WanQuanErChaShuDeQuanZhi.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//

#include 

using namespace std;

int main() {
     
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n;
    cin >> n;
    int level = log2(n) + 1;
    int ans = -0x3f3f3f3f, pos;
    for (int i = 1; i <= level; i++) {
     
        int tot = pow(2, i - 1);
        int sum = 0;
        if (i == level)
            tot = n - (int) pow(2, level - 1) + 1;
        for (int j = 1, t; j <= tot; j++) {
     
            cin >> t;
            sum += t;
        }
        if (sum > ans) {
     
            ans = sum;
            pos = i;
        }
    }
    cout << pos << endl;
    return 0;
}

H 等差数列

我们将数列从小到大排序,显然在等差数列中对于任意两对连续两数之差,都不可能互素,所以最小的差值即为使等差数列最短的公差。

注意当公差为0时,如果采用除法计算将会除法除 0 0 0错误,此时需特判长度为 n n n.

//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:DengChaShuLie.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//

#include 

using namespace std;

vector<int> v;

int main() {
     
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n;
    cin >> n;
    v.resize(n);
    for (int &it:v) cin >> it;
    int delta = 0x7fffffff;
    sort(v.begin(), v.end());
    for (int i = 1; i < v.size(); i++)
        delta = min(delta, v[i] - v[i - 1]);
    if (delta == 0) cout << n << endl;
    else cout << (v.back() - v[0]) / delta + 1 << endl;
    return 0;
}

I 后缀表达式

这跟后缀表达式有什么关系,又不用输出最终表达式……

一开始想简单了,脑补了一棵树:

SUB
SUB
SUB
SUB
PLUS
PLUS
PLUS
7
5
8
6
4
3
1
2

1 ∼ 8 1\sim 8 18代表输入的数从大到小编号后的数。整颗表达式树化简后最终为 1 + 2 + 3 + 4 + 5 + 6 + 7 − 8 1+2+3+4+5+6+7-8 1+2+3+4+5+6+78,也就是说减去了个最小的数。看上去很对,但事实上这只对非负数成立,上面这种贪心方式有问题。所以我莽的一发只拿了45分,不出意外这45分数据都是只含非负数的(错误方法水45???)。

不过上面这个方法也同时给了我们启发,上面贪心错误的原因是我们可能加了负数。不过仔细想一想,这里面有一个很显然的性质:如果在SUB节点下,我们把负数安排在右孩子位置,则这个操作相当于PLUS这个负数的绝对值。

同样,如果是SUB节点后跟一系列操作符,展开表达式后这些操作符都会变号。

所以我们很容易能想到新的贪心——在有负号的情况下,我们可以将任意数量的PLUS变为SUB,同时因为SUB的存在左右置换优化,所以实际上我们可以把情况统一为PLUS绝对值。

当负号个数为0时,整个解的情况显然是全部的和;当负号个数大于0时,除了唯一一个无法消去的SUB外,其它所有的操作符和数都可以用上面的方法调整为PLUS绝对值。那么唯一的负号和肯定有的正号应该给哪些数呢?当然是SUB最小的那个数,PLUS最大的那个数。如果讲整个数列排序,则最优表达式的值为 ∑ i = 2 n − 1 ∣ v [ i ] ∣ − v [ 0 ] + v [ n ] \sum\limits_{i=2}^{n-1}|v[i]|-v[0]+v[n] i=2n1v[i]v[0]+v[n].

这题值得总结一下,如果现场直接莽可能就翻了。

//
// Created by Visors on 2020/10/15.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:HouZhuiBiaoDaShi.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//

#include 

using namespace std;

vector<int> v;

int main() {
     
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n, m;
    cin >> n >> m;
    v.resize(n + m + 1);
    long long ans = 0;
    for (int &it:v) cin >> it;
    if (m == 0) {
     
        for (int &it:v) ans += it;
    } else {
     
        sort(v.begin(), v.end());
        ans = -v[0] + v.back();
        for (int i = 1; i < v.size() - 1; i++) ans += abs(v[i]);
    }
    cout << ans << endl;
    return 0;
}

J 灵能传输

这题有点难。

首先题目给出了一个连续三个数的操作,注意到这个操作不会使三个数的和发生变化,由是我们想到观察其前缀和。假设原数为 ( a i − 1 , a i , a i + 1 ) (a_{i-1},a_i,a_{i+1}) (ai1,ai,ai+1),则操作后数为 ( a i − 1 + a i , − a i , a i + 1 + a i ) (a_{i-1}+a_i,-a_i,a_{i+1}+a_i) (ai1+ai,ai,ai+1+ai)。原前缀和为 ( S i − 1 , S i , S i + 1 ) (S_{i-1},S_i,S_{i+1}) (Si1,Si,Si+1),操作后为 ( S i − 1 + a i , S i + a i − 2 a i , S i + 1 + a i − 2 a i + a i ) (S_{i-1}+a_i,S_i+a_i-2a_i,S_{i+1}+a_i-2a_i+a_i) (Si1+ai,Si+ai2ai,Si+1+ai2ai+ai),整理后即为 ( S i , S i − 1 , S i + 1 ) (S_i,S_{i-1},S_{i+1}) (Si,Si1,Si+1)

我们发现这样操作三个数其实只是将它们前两个数的前缀和交换,那么我们不妨将这一三元操作转换为二元操作看待。转换的同时我们将求解目标也转换一下,即原来要求 max ⁡ ( ∣ a i ∣ ) \max(|a_i|) max(ai),现在要求 max ⁡ ( ∣ S i − S i − 1 ∣ ) \max(|S_i-S_{i-1}|) max(SiSi1)。那么如何取得该值呢?其实贪心就可以,这个描述起来有点复杂,可以先参考下面的代码,后面有时间我再详细补充。

//
// Created by Visors on 2020/10/16.
//
// 题目名:TODO
// 题目来源:TODO
// 题目链接:TODO
// 算法:LingNengChuanShu.cpp
// 用途:TODO
// 时间复杂度:O(TODO)
//

#include 

using namespace std;

typedef long long ll;

int T, n;
vector<ll> v, preSum, s;
vector<bool> book;

int main() {
     
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> T;
    while (T--) {
     
        cin >> n;
        v.resize(n);
        preSum.resize(n + 1);
        s.resize(n + 1);
        book.resize(n + 1);
        fill(book.begin(), book.end(), false);
        for (ll &it:v) cin >> it;
        preSum[0] = 0;
        for (int i = 1; i <= n; i++)
            preSum[i] = preSum[i - 1] + v[i - 1];
        ll s0 = preSum[0], sn = preSum[n];
        if (s0 > sn) swap(s0, sn);
        sort(preSum.begin(), preSum.end());
        for (int i = 0; i <= n; i++)
            if (preSum[i] == s0) {
     
                s0 = i;
                break;
            }
        for (int i = n; i >= 0; i--)
            if (preSum[i] == sn) {
     
                sn = i;
                break;
            }
        ll ans = -0x7fffffffffffffff;
        int l = 0, r = n;
        for (int i = s0; i >= 0; i -= 2) {
     
            s[l++] = preSum[i];
            book[i] = true;
        }
        for (int i = sn; i <= n; i += 2) {
     
            s[r--] = preSum[i];
            book[i] = true;
        }
        for (int i = 0; i <= n; i++)
            if (!book[i])
                s[l++] = preSum[i];
        for (int i = 1; i <= n; i++) ans = max(ans, abs(s[i] - s[i - 1]));
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(算法,蓝桥)