2019年第十届蓝桥杯c/c++B组 详细题解

组队


这个题目直接手算,直接出答案。
可以使用PDF修改功能,也可以在电脑中寻找这种可以修改的软件,直接在上面做笔记。
在这里插入图片描述

填空答案

答案:97 + 99 + 98 + 98 + 98 = 490

年号字串


思路

根据题意,我们可以知道,这个就是26进制的写法,手算将 201 9 ( 26 ) 2019_{(26)} 2019(26),使用短除法即可得出答案。

填空答案

答案:BYQ

数列求值


思路

利用计算机的特性, 能够短时间枚举大量情况,不断的计算,边计算边取模即可得出答案。

填空答案

答案:4659

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 20190330, mod = 10000;
int f[N] = {1, 1, 1, 1};

int main(){
	for (int i = 4; i <= 20190324; ++i)
		f[i] = ((f[i -1]   + f[i - 2] )% mod+ f[i - 3]) % mod;
	cout << f[20190324] << endl;
	return 0;
}

数的分解


题目思路

交换顺序后算一种的话,我们就可以自己将顺序定好,使他们成单调性, 这样就免去了判重的功能。
然后还不能出现2和4,写个函数判断一下就好了。

填空答案

答案:40785

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)

bool judge(int x, int n){
	while(x){
		if (x % 10 == n) return false;
		x /= 10; 
	}
	return true;
}

int main(){
	int ans = 0;
	_rep(i, 1, 2019) for(int j = i + 1; i + j <= 2019; ++j) for(int k = j + 1; i + j + k <= 2019; ++k) 
	if (i + j + k == 2019 && judge(i, 2) && judge(j, 2) && judge(k,2)&& judge(i, 4) && judge(j, 4) && judge(k,4)) ans++;
	cout << ans << endl;
	return 0;
}

迷宫


思路

分析题目: 求路径所对应的字符串字典序最小,那么也就是在长度最小的情况下保证方向所对应的英文字符是有效方向的最小。
对应算法: BFS,求最小路问题。
具体思路:用一个string 保存路径并判断时候走过,不用怕超时间或者超空间,因为这是填空题,只要知道答案就行。BFS模板套上,结束!

填空答案

答案:DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 50 + 10;
int dx[] = { 1, 0, 0, -1 }, dy[] = { 0, -1, 1, 0 }, g[N][N], n, m;
char dir[] = "DLRU";
string dist[N][N];

bool inside(int x, int l, int r) {
    return l <= x && x <= r;
}

string bfs() {
    queue<pii> q;
    q.push({ 0, 0 });

    while (!q.empty()) {
        pii u = q.front();
        q.pop();

        int x = u.first, y = u.second;
        string distance = dist[x][y];
        if (x == n - 1 && y == m - 1) return distance;

        _for(i, 0, 4) {
            int a = x + dx[i], b = y + dy[i];
            if (inside(a, 0, n - 1) && inside(b, 0, m - 1) && !g[a][b] && dist[a][b] == "") {
                dist[a][b] = distance + dir[i];
                
                q.push({ a, b });
            }
        }
    }
    return "";
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false); // 取消cin与stdin 的同步
    cout.tie(0), cin.tie(0);

    cin >> n >> m;
    _for(i, 0, n) _for(j, 0, m) scanf("%1d", &g[i][j]);
    cout << bfs() << ENDL;
    return 0;
}

特别数的和


题目提交点

点我进入官网提交点

思路

暴力枚举加个判断即可。

代码

#include<iostream>
using namespace std;

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

int main(){
    int n;
    cin>>n;
    
    int ans=0;
    for(int i=1;i<=n;i++)
    if(judge(i)) ans+=i;
    
    cout<<ans<<endl;
    return 0;
}

完全二叉树的权值


提交点

点我进入官网提交

思路

通过观察发现,每层节点的数量都是上一层的节点数量乘以2,那么就可以得出,设当前层数位d,当前层第一个数的下标是i,那么,我们每次直接遍历 [ i , i + 2 d − 1 ] [i, i + 2^{d - 1}] [i,i+2d1],算出总和。用一个变量保存数量最大值,一个变量保存最大数值的层数。
最终结果输出层数即可。

代码

#include<iostream>
#include<cstdio>
using namespace std;

typedef long long LL;
const int N = 100010;
int w[N];

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &w[i]);
	
	LL max_w = w[1];
	int ans = 1;

	for (int d = 2, i = 2; i <= n; ++d, i *= 2) {
		LL sum = 0;
		for (int j = i; j < i + (1 << (d - 1)) && j <= n; ++j) sum += w[j];
		if (sum > max_w) max_w = sum, ans = d;
	}
	printf("%d\n", ans);
	return 0;
}

等差数列


提交点

点我进入官网提交

思路

求包含这N个数的最短序列长度。
也就是求排序后每个前后两个数之间差值的最小公约数,也就是求最小公差的问题。
打蓝桥杯比赛一定要细心,多给自己出几个恶心的数据点,因为只能提交一次。

5
0 0 0 0 0

这个样例答案是5喔。

最短的话,一定是最小的那个数放在最前面,作为数列的第一项,最后答案的公式为:
b 1 = x d , b n = y d = > n = ( b 1 − b n ) / d + 1 b_1 = xd, b_n = yd => n = (b_1 - b_n) / d + 1 b1=xd,bn=yd=>n=(b1bn)/d+1

代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 100000 + 10;
int a[N];

int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false); // 取消cin与stdin 的同步
    cout.tie(0), cin.tie(0);

    int n;
    cin >> n;
    _for(i, 0, n) cin >> a[i];
    
    sort(a, a + n);
    int ans = 0;
    _for(i, 1, n) ans = gcd(ans, a[i] - a[i - 1]);

    if (!ans) cout << n << ENDL;
    else cout << (a[n - 1] - a[0]) / ans  + 1<< ENDL;
    return 0;
}

后缀表达式


题目提交点

点我进入官网提交

题目大意

普及一下后缀表达式:
一般算术表达式写法 ( 8 − 9 ) × 3 + 5 (8 - 9) \times 3 + 5 (89)×3+5,这个是我们很容易就辨别的表达式, 也就是中序遍历的结果。

但是对于电脑而言,这种写法太复杂了,所以计算机计算这种算术表达式是使用后序遍历的,这个表达式在电脑中是这样的 8 9 − 3 × 5 + 8 \quad 9 - 3 \times 5 + 893×5+
它是怎么运算的呢?先创建一个空栈, 遇到数字就入栈,遇到符号就将栈顶两个元素进行运算。
它也可以转化为一棵树,数字是叶子节点, 符号是内部节点,根据这个原则就可以构造出一颗数。
但是这个题目不需要使用树的知识去解决这个问题, 就是告诉你任意合法的算术表达式都可以。

题目简化意思:
给定n个加号、m个减号以及一个数组且可以使用括号,问:组成的表达式中运算后最大的值是多少?

思路

题目算法分析: 贪心算法

贪心策略分析:
我们希望负号尽可能多的在减号前面, 加号尽可能能在加号前面。
我们发现,如果能使用括号,那么减号可能会变成负号。所以尝试对负号进行分析:

  • m = 0 m = 0 m=0时, 说明没有负号,只有正号,正号就算加括号也改变不了符号,所以只能是所有情况相加。
  • 我们考虑:当 m > 0 m > 0 m>0时, 不加括号的时候,可以构造m个减号;如果加了括号的话,可以将某些负号变成正号。结合两种可以得出:当 m > 0 m > 0 m>0时,可以构造出 1 ∼ m 1 \sim m 1m个减号。
  • 我们再进一步考虑:当 m > 0 , n > = 0 m > 0, n >= 0 m>0,n>=0 时,我们可以将加号放到括号里面去,就会变成正号, 也就是说可以构造出 1 ∼ ( m + n ) 1 \sim(m + n) 1(m+n)个减号。

第一个数是放在最前面的,所以说这个数一定得要是最大的数。
如果存在负号的话,至少会出现一个负号,所以要减去一个最小的数。
其余的数一定是取绝对值,因为负号可以将负数变成正数。

这个题目还是有很多人没有想出这个贪心策略,想要一次性想出这个题,还是得要一定的刷题量,但是如果耐下性子去寻找样例,不断的分析,还是可以找出规律,然后做出来的。

代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 200000 + 10;
ll a[N];

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false); // 取消cin与stdin 的同步
    cout.tie(0), cin.tie(0);

    int n, m;
    cin >> n >> m;
    int sz = n + m + 1;

    ll ans = 0;
    _for(i, 0, sz) cin >> a[i];

    if (!m) _for(i, 0, sz) ans += a[i];
    else {
        sort(a, a + sz);
        ans = a[sz - 1] - a[0];
        _for(i, 1, sz - 1) ans += abs(a[i]);
    }

    cout << ans << ENDL;
    return 0;
}

灵能传输


题目提交点

点我进入官网提交

题目大意

给定一个序列a, 可对小标为 [ 2 , n − 1 ] [2, n - 1] [2,n1]中的数进行 a i − 1 + = a i a i − = 2 a i a i + 1 + = a i a_{i - 1} += a_i \\ a_i -= 2a_i \\ a_{i+1} += a_i ai1+=aiai=2aiai+1+=ai操作,求每次操作过后的数组中的最大值的最小值。

思路

  • 题目算法分析

我们发现,他这么操作,有些数的前缀和是不变的, 所以尝试使用前缀和去分析,因为要求最大值的最小值,所以,可能会用到贪心。
综上所述:前缀和 + 贪心。

  • 前缀和分析:

分析发现前缀和 s i − 1 s_{i-1} si1 会变成 s i s_{i} si s i s_{i} si 会变成 s i − 1 s_{i - 1} si1 s i + 1 s_{i +1} si+1不变, 也就是说 s i s_{i} si s i − 1 s_{i - 1} si1交换位置了,我们所要求的 a i = s i − s i − 1 a_i = s_{i} - s_{i - 1} ai=sisi1, 那么就是求交换顺序之后的两两之间的差值的最大值最小。

  • 贪心分析

什么情况下最大值最小呢?
将前缀和进行排序,排序之后连续两个之间的差值最小。
因为首尾两个数值是不能交换的,所以当 s 1 s_1 s1 < s n s_n sn时,呈现的图像是倒N字型的曲线(用是 s i s_i si - i组成的图形)。
那么在前缀和数组中组成的数还是具有单调性的,(我们假设 s 1 s_1 s1 < s n s_n sn,那么图像是呈单调递增的。)

要形成倒N字型的图形的话,需要拿一些值补在 s 1 s_1 s1和最小值之间,那么取哪一些值呢?
贪心策略隔一个字取一个,因为要走来回。

这个题目以及他的贪心证明需要画图,以视频的形式讲解,不过这篇文章发的太晚了,做了视频也不会有太多人看,所以暂时鸽了。
如果你觉得我之前的文章讲的可以,你可以滴滴我,人多的话,我就出个视频,hhh~

代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 300000 + 10;
ll s[N], bak[N];
bool vis[N];

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false); // 取消cin与stdin 的同步
    cout.tie(0), cin.tie(0);
    
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        memset(s, 0, sizeof s);
        memset(vis, 0, sizeof vis);
        _rep(i, 1, n) {
            cin >> s[i];
            s[i] += s[i - 1]; 
        }

        ll s0 = s[0], sn = s[n]; // s1也是可以交换的,所以从s0开始
        if (s0 > sn) swap(s0, sn);
        sort(s, s + n + 1);

        _rep(i, 0, n) if (s[i] == s0) {
            s0 = i;
            break;
        }
        _rep(i, 0, n) if (s[i] == sn) {
            sn = i;
            break;
        }

        int l = 0, r = n;
        for (int i = s0; i >= 0; i -= 2) {
            vis[i] = true;
            bak[l++] = s[i];
        }
        for (int i = sn; i <= n; i += 2) {
            vis[i] = true;
            bak[r--] = s[i];
        }
        _rep(i, 0, n) if (!vis[i]) bak[l++] = s[i];

        ll ans = 0;
        _rep(i, 1, n) ans = max(ans, abs(bak[i] - bak[i - 1]));
        cout << ans << ENDL;
    }

    return 0;
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值