【算法打卡day39(2026-04-06~08 周一~周三)】(10道蓝桥杯真题)今日练习:蓝桥杯第13届省赛B组Cpp组

- 第 202 篇 -
Date: 2026 - 04 - 06~08 | 周一~周三
Author: 郑龙浩(仟墨)
今日练习:蓝桥杯第13届省赛B组Cpp组 10道题

6号刷题后,7号弄懂了大半错题,8号解决了剩下的部分。虽然DP题型仍不太理解,但我决定先放一放,转而练习贪心算法去了,因为贪心一直没有练习。考虑到蓝桥杯只剩两天,最后8号(今天)晚上我决定集中复习之前做过的题目。

2026-04-06~08-算法打卡day38day39-蓝桥杯第13届省赛B组Cpp组

文章目录

1-九进制转十进制

全对

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

九进制正整数 (2022)9(2022)9​ 转换成十进制等于多少?

  • 最大运行时间:1s
  • 最大运行内存: 512M
cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 1-九进制转十进制
 * Author:郑龙浩
 * Date:2026-04-06
 */

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << 2 * pow(9, 0) + 2 * pow(9, 1) + 2 * pow(9, 3);
    return 0;
}

2-顺子日期

0分

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

小明特别喜欢顺子。顺子指的就是连续的三个数字:123、456 等。顺子日期指的就是在日期的 yyyymmdd 表示法中,存在任意连续的三位数是一个顺子的日期。例如 20220123 就是一个顺子日期,因为它出现了一个顺子:123; 而 20221023 则不是一个顺子日期,它一个顺子也没有。小明想知道在整个 2022 年份中,一共有多少个顺子日期?

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 512M

0分版本

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 2-顺子日期-0分
 * Author:郑龙浩
 * Date:2026-04-06
 */

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    // 22 年是平年
    int months[13] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    string s;
    int cnt = 0;
    for (int i = 1; i < 13; i++) {
        for (int j = 1; j <= months[i]; j++) {
            if (i > 9 && j > 9) s = "2020" + to_string(i) + to_string(j);
            else if (i > 9 && j < 10) s = "2020" + to_string(i) + '0' + to_string(j);
            else if (i < 10 && j > 9) s = "2020" + '0' + to_string(i) + to_string(j);
            else if (i < 10 && j < 10) s = "2020" + '0' + to_string(i) + '0' + to_string(j);
            for (int k = 0; k <= s.size() - 3; k++) {
                if (s[k] != '0' && s[k] + 1 == s[k + 1] && s[k + 1] + 1 == s[k + 2]) { cnt++; break;}
            }
        }
    }
    cout << cnt;
    return 0;
}

修改后

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 2-顺子日期-修改版
 * Author:郑龙浩
 * Date:2026-04-06
 * 错因1:months忘记在开头写0了
 * 错因2:2022写错为了2020
 * 错因3:"2022" + '0' 并不是20220,而是2022字面量奖赏'0'的ASCII值48,得出-->d,导致存储的不是字符串拼接,而是2022字面量+'0'ASCII
 *          我后来改成了 "2022" + "0"也是错误的,因为两个字面量相加就不对,应该将其中一个改为string,这样就是string + 字面量,而不是字面量+字面量了
 */

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    // 22 年是平年
    int months[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    string s;
    int cnt = 0;
    for (int i = 1; i < 13; i++) {
        for (int j = 1; j <= months[i]; j++) {
            if (i > 9 && j > 9) s = "2022" + to_string(i) + to_string(j);
            else if (i > 9 && j < 10) s = "2022" + to_string(i) + "0" + to_string(j);
            else if (i < 10 && j > 9) s = "2022" + string("0") + to_string(i) + to_string(j);
            else if (i < 10 && j < 10) s = "2022" + string("0") + to_string(i) + "0" + to_string(j);
            // 错误代码2:在C++中不允许出现"2022" + "0",因为这个是两个字符串字面量的加法,不允许链接两个const char*指针
            // 不能直接对两个字符串字面量使用 +运算符
            // else if (i < 10 && j > 9) s = "2022" + "0" + to_string(i) + to_string(j);
            // else if (i < 10 && j < 10) s = "2022" + "0" + to_string(i) + "0" + to_string(j);
            // 错误代码1:不能 string 字面量 + ASCII
            // else if (i > 9 && j < 10) s = "2022" + to_string(i) + '0' + to_string(j);
            // else if (i < 10 && j > 9) s = "2022" + '0' + to_string(i) + to_string(j);
            // else if (i < 10 && j < 10) s = "2022" + '0' + to_string(i) + '0' + to_string(j);
            for (int k = 0; k <= s.size() - 3; k++) {
                if (s[k] + 1 == s[k + 1] && s[k + 1] + 1 == s[k + 2]) {cnt++; break;}
            }
        }
    }
    cout << cnt;
    return 0;
}

3-刷题统计

10个案例过4个

问题描述

小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天 做 aa 道题目, 周六和周日每天做 bb 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数大于等于 nn 题?

输入格式

输入一行包含三个整数 a,ba,b 和 nn.

输出格式

输出一个整数代表天数。

样例输入

复制代码
10 20 99

样例输出

text 复制代码
8

评测用例规模与约定

对于 50%50% 的评测用例, 1≤a,b,n≤1061≤a,b,n≤106.

对于 100%100% 的评测用例, 1≤a,b,n≤10181≤a,b,n≤1018.

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

10个案例过4个

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 3-刷题统计-10案例过4个
 * Author:郑龙浩
 * Date:2026-04-06
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b, n;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> a >> b >> n;
    int zhouji = 1;
    ll sum = 0;
    for (ll day = 1; ; day++, zhouji = (zhouji + 1) % 8) {
        if (zhouji == 6 || zhouji == 7) sum += b;
        else sum += a;
        if (sum >= n) {cout << day; return 0;}
    }
    return 0;
}

全对版本

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 3-刷题统计-修改版
 * Author:郑龙浩
 * Date:2026-04-07修改
 * 错因1: (zhouji + 1) % 8 会让周几在0~7之间循环,明显错误
 * 错因2:当n为1e18的时候,明显会超时
 * 修改方法:直接计算可以解决多少个以7天为单位的题目,然后只循环最后一周
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
/*
ll a, b, n;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> a >> b >> n;
    // 修改前:int zhouji = 1;
    int zhouji = 0;

    ll sum = 0;
    // 错误代码:这样day会在0~7之间徘徊,所以不对
    for (ll day = 1; ; day++, zhouji = (zhouji + 1) % 8) {
        if (zhouji == 6 || zhouji == 7) sum += b;
        else sum += a;
        if (sum >= n) {cout << day; return 0;}
    }
    return 0;
}*/
ll a, b, n;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin >> a >> b >> n;
    int totalWeek = a * 5 + b * 2; // 一周可以做的题的数量
    ll totalDay = n / totalWeek * 7; // 总做题天数,7天都做
    ll remaining = n % totalWeek; // 还剩多少题没做
    if (remaining == 0) {cout << totalDay; return 0;}
    for (int day = 0; ; day = (day + 1) % 7) { // 0~6对应1~7周
        totalDay++;
        if (day == 5 || day == 6) remaining -= b;
        else remaining -= a;
        if (remaining <= 0) {cout << totalDay; return 0;}
    }
}

4-修剪灌木

8案例过3-内存太大

问题描述

爱丽丝要完成一项修剪灌木的工作。

有 NN 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晩会修剪一棵灌 木, 让灌木的高度变为 0 厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始, 每天向右修剪一棵灌木。当修剪了最右侧的灌木后, 她会调转方向, 下一天开 始向左修剪灌木。直到修剪了最左的灌木后再次调转方向。然后如此循环往复。

灌木每天从早上到傍晩会长高 1 厘米, 而其余时间不会长高。在第一天的 早晨, 所有灌木的高度都是 0 厘米。爱丽丝想知道每棵灌木最高长到多高。

输入格式

一个正整数 NN, 含义如题面所述。

输出格式

输出 NN 行, 每行一个整数, 第 ii 行表示从左到右第 ii 棵树最高能长到多高。

样例输入

复制代码
3

样例输出

text 复制代码
4
2
4

评测用例规模与约定

对于 30%30% 的数据, N≤10N≤10.

对于 100%100% 的数据, 1<N≤100001<N≤10000.

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 512M

8案例过3个,超时

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 4-修剪灌木-8案例过3-内存太大
 * Author:郑龙浩
 * Date:2026-04-06
 * N 棵树 每天修剪一棵 
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    set <vector <int>> set; // 达到过的高度
    int n; cin >> n;
    vector <int> nums(n, 0);
    set.insert(nums);
    int pos = 0;
    bool direction = true; // 方向
    while (true) {
        nums[pos] = 0;
        // for (int it : nums) cout << it << ' '; cout << '\n';
        for (int i = 0; i < n; i++) nums[i] = nums[i] + 1;
        // for (int it : nums) cout << it << ' '; cout << '\n' << '\n';
        if (set.find(nums) != set.end()) {// 如果循环回来了
            // for (int it : nums) cout << it << ' '; cout << '\n';
            break;
        } else set.insert(nums);

        // 确定下一个pos位置
        if (direction) {
            if (pos == n - 1) {pos = n - 2; direction = false;}
            else pos++;
        } else {
            if (pos == 0) {pos = 1; direction = true;}
            else pos--;
        }
    }
    // cout << set.size() << '\n';
    vector <vector <int>> new_nums(set.begin(), set.end());
    // cout << new_nums.size() << '\n';
    for (int j = 0; j < n; j++) {
        int Max = 0;
        for (int i = 0; i < new_nums.size(); i++) {
            Max = max(new_nums[i][j], Max);
        }
        cout << Max << '\n';
    }
    return 0;
}

全对

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 4-修剪灌木-8案例过3-修改版-原来是道数学题
 * Author:郑龙浩
 * Date:2026-04-07修改
 * 这是一道数学题,我刚开始以为是个模拟题,就按照最笨的方法存储了多个数组,我其实知道这样会超过内存限制,但是没想到其他的好方法
 * 等这次模拟比赛交卷后,我发现自己就对了3个案例,其他全部超过内存限制
 * 然后问了AI,我才知道,这TM原来是个数学题,我就没往这方面去想,或者不是数学题,就是稍微转换一下思路就行了,我还以为要什么算法呢,想得太复杂了
 * 正确的思考过程是:
 * 一个树,最大高度是,将其修剪后为0,然后
 * 可能1,向右走:向右遍历到头,然后再回来,经过天数为 2 * (N - i)
 * 可能2,向左走:向左遍历到头,然后再回来,经过天数为 2 * (i - 1)
 * 天数就是数的高度,因为每天+1
 * 所以求出max(2 * (N - i), 2 * (i - 1))即可,保留其中最大的值
 * 
 * 思维还是太固化了,不变通,没有那么灵敏
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    /*
    set <vector <int>> set; // 达到过的高度
    int n; cin >> n;
    vector <int> nums(n, 0);
    set.insert(nums);
    int pos = 0;
    bool direction = true; // 方向
    while (true) {
        nums[pos] = 0;
        // for (int it : nums) cout << it << ' '; cout << '\n';
        for (int i = 0; i < n; i++) nums[i] = nums[i] + 1;
        // for (int it : nums) cout << it << ' '; cout << '\n' << '\n';
        if (set.find(nums) != set.end()) {// 如果循环回来了
            // for (int it : nums) cout << it << ' '; cout << '\n';
            break;
        } else set.insert(nums);

        // 确定下一个pos位置
        if (direction) {
            if (pos == n - 1) {pos = n - 2; direction = false;}
            else pos++;
        } else {
            if (pos == 0) {pos = 1; direction = true;}
            else pos--;
        }
    }
    // cout << set.size() << '\n';
    vector <vector <int>> new_nums(set.begin(), set.end());
    // cout << new_nums.size() << '\n';
    for (int j = 0; j < n; j++) {
        int Max = 0;
        for (int i = 0; i < new_nums.size(); i++) {
            Max = max(new_nums[i][j], Max);
        }
        cout << Max << '\n';
    } */
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cout << max(2 * (i - 1), 2 * (n - i)) << endl;
    }
    return 0;
}

5-X进制减法

不会做,现在会做了

问题描述

进制规定了数字在数位上逢几进一。

XX 进制是一种很神奇的进制, 因为其每一数位的进制并不固定!例如说某 种 XX 进制数, 最低数位为二进制, 第二数位为十进制, 第三数位为八进制, 则 XX 进制数 321 转换为十进制数为 65 。

现在有两个 XX 进制表示的整数 AA 和 BB, 但是其具体每一数位的进制还不确 定, 只知道 AA 和 BB 是同一进制规则, 且每一数位最高为 NN 进制, 最低为二进 制。请你算出 A−BA−B 的结果最小可能是多少。

请注意, 你需要保证 AA 和 BB 在 XX 进制下都是合法的, 即每一数位上的数 字要小于其进制。

输入格式

第一行一个正整数 NN, 含义如题面所述。

第二行一个正整数 MaMa​, 表示 XX 进制数 AA 的位数。

第三行 MaMa​ 个用空格分开的整数, 表示 XX 进制数 AA 按从高位到低位顺序各 个数位上的数字在十进制下的表示。

第四行一个正整数 MbMb​, 表示 XX 进制数 BB 的位数。

第五行 MbMb​ 个用空格分开的整数, 表示 XX 进制数 BB 按从高位到低位顺序各 个数位上的数字在十进制下的表示。

请注意, 输入中的所有数字都是十进制的。

输出格式

输出一行一个整数, 表示 XX 进制数 A−BA−B 的结果的最小可能值转换为十进 制后再模 1000000007 的结果。

样例输入

复制代码
11
3
10 4 0
3
1 2 0

样例输出

text 复制代码
94

样例说明

当进制为: 最低位 2 进制, 第二数位 5 进制, 第三数位 11 进制时, 减法 得到的差最小。此时 AA 在十进制下是 108,B108,B 在十进制下是 14 , 差值是 94。

评测用例规模与约定

对于 30%30% 的数据, N≤10;Ma,Mb≤8N≤10;Ma​,Mb​≤8.

对于 100%100% 的数据, 2≤N≤1000;1≤Ma,Mb≤100000;A≥B2≤N≤1000;1≤Ma​,Mb​≤100000;A≥B.

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M
cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 5-X进制减法-不会做-现在会做了
 * Author:郑龙浩
 * Date:2026-04-07 会做
 * 这道题,我就没读懂题意 
 * 或者说,我就没看出来「某种 X 进制数, 最低数位为二进制, 第二数位为十进制, 第三数位为八进制, 则 X 进制数 321 转换为十进制数为 65」
 * 321是怎么转换为65的,没搞懂这个规则,后面的做法就不用想了,想靠暴力拿部分分都不可能存在了
 * 
 * X进制 -> 十进制 转换规则:
 * 例子: X进制的321(从左到右是百位、十位、个位)
 * 1:二进制, 2:十进制, 3:百进制
 * 
 * 权重的概念:某位数字假设为1,代表的十进制值是多少
 * 个位:权重1
 * 十位:权重是「个位的进制」
 * 百位:权重是「个位进制 * 十位进制」
 * 
 * 实际计算:
 * 数字   十进制
 * 个位1:1
 * 十位2:2 * (1 * 2) -> (2 * 1 * 2进制)
 * 百位3:3 * (2 * 10) -> (3 * 2 * 10进制)
 * 相加后为:
 * 1 + 2 * (1 * 2) + 3 * (2 * 10) = 65
 * 
 * 知道了进制转换规则后
 * 
 * 还有一个地方要思考,就是该如何确定 A - B 是最小的呢
 * 
 * 当然,需要先将A 和 B转换为十进制后相减,这个相减的值要尽可能的最小
 * 
 * 我刚开始想的是,让每个位都从2~10进制遍历一遍,求出最小的差值来,但是这样一定会超时且内存爆炸
 * 
 * 正确的思路应该是:
 * 1 从最低位到最高位,独立确定每位的进制。
 * 2 对第 i 位:
        1)进制至少要比 A 和 B 在这一位的数字大(因为数字必须小于进制)
        2)所以进制最小值 = max(AiJinzhi, Bijinzhi) + 1
        3)同时进制 ≥ 2
        4)且进制 ≤ N
        5)就选这个最小值进制       
 * 3 这样选的进制保证 A 和 B 合法,且让 A?B 最小。
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 100000 + 5;
const int MOD = 1000000007;
int N, Acnt, Bcnt;
vector <int> A(M, 0), B(M, 0);
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> N >> Acnt;
    for (int i = 0; i < Acnt; i++) cin >> A[i];
    cin >> Bcnt;
    for (int i = 0; i < Bcnt; i++) cin >> B[i];
    // 为了数组对齐,需要逆转数组 & 也可以保证如果Acnt和Bcnt长度不同,没有数字的位置是0,此时就不会出错了
    // 写错了,因为要求对其,后面的0不要
    // reverse(A.begin(), A.end());
    // reverse(B.begin(), B.end());
    reverse(A.begin(), A.begin() + Acnt);
    reverse(B.begin(), B.begin() + Bcnt);

    // 处理第i个数字的进制是多少,处理i-1的十进制是多少
    ll Anum = 0, Bnum = 0;
    ll weight = 1; // 第i位的权重,个位权重肯定是1,所以权重是1,当前位的权重等于前面所有的进制乘积
    int len = max(Acnt, Bcnt);
    for (int i = 0; i < len; i++) {
        int Ai = (i < Acnt) ? A[i] : 0; // Ai是多少,如果大于Acnt就是0
        int Bi = (i < Bcnt) ? B[i] : 0;
        // 确定当前的进制
        int base = max(2, max(Ai, Bi) + 1); // 最少为2进制,所以要保证不出错
        if (base > N) base = N; // 最高为N进制(题目限制)
        Anum = (Anum + Ai * weight) % MOD;
        Bnum = (Bnum + Bi * weight) % MOD;

        // 更新权值: 下一位的权值等于当前进制 * 前面的权值
        weight = (weight * base) % MOD;
    }
    // 错误代码:因为可能会出现A - B 为负数的情况(因为数学上模运算是没有负数的)
    
    // cout << (Anum - Bnum) % MOD; 错误写法
    // 如果有负数,应该是这样计算的 -2 % 10 = (-2 + 10) % 10 = 8
    cout << (Anum - Bnum + MOD) % MOD;
    return 0;
}

6-统计子矩阵

问题描述

给定一个 N×MN×M 的矩阵 AA, 请你统计有多少个子矩阵 (最小 1×11×1, 最大 N×M)N×M) 满足子矩阵中所有数的和不超过给定的整数 KK ?

输入格式

第一行包含三个整数 N,MN,M 和 KK.

之后 NN 行每行包含 MM 个整数, 代表矩阵 AA.

输出格式

一个整数代表答案。

样例输入

复制代码
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12

样例输出

text 复制代码
19

样例说明

满足条件的子矩阵一共有 19 , 包含:

大小为 1×11×1 的有 10 个。

大小为 1×21×2 的有 3 个。

大小为 1×31×3 的有 2 个。

大小为 1×41×4 的有 1 个。

大小为 2×12×1 的有 3 个。

评测用例规模与约定

对于 30%30% 的数据, N,M≤20N,M≤20.

对于 70%70% 的数据, N,M≤100N,M≤100.

对于 100%100% 的数据, 1≤N,M≤500;0≤Aij≤1000;1≤K≤2500000001≤N,M≤500;0≤Aij​≤1000;1≤K≤250000000.

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

10个案例过6个案例

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 6-统计子矩阵-10案例过6个
 * Author:郑龙浩
 * Date:2026-04-06 
 * 二维的前缀和
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll nums[30][110] = {0};
int N, M;
ll K;
ll S(int x1, int y1, int x2, int y2) {
    if (x1 == 0 && x2 == 0) return nums[x2][y2];
    if (x1 == 0) return nums[x2][y2] - nums[x2][y1 - 1];
    if (y1 == 0) return nums[x2][y2] - nums[x1 - 1][y2];
    return nums[x2][y2] - nums[x1 - 1][y2] - nums[x2][y1 - 1] + nums[x1 - 1][y1 - 1];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> N >> M >> K;
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) cin >> nums[i][j];
    for (int i = 0; i < N - 1; i++) nums[i + 1][0] += nums[i][0];
    for (int j = 1; j < M; j++) nums[0][j] += nums[0][j - 1];
    for (int i = 1; i < N; i++) for (int j =1; j < M; j++)
        nums[i][j] = nums[i - 1][j] + nums[i][j - 1] + nums[i][j] - nums[i - 1][j - 1];

    int cnt = 0;
    for (int x1 = 0; x1 < N; x1++) for (int y1 = 0; y1 < M; y1++)
    for (int x2 = x1; x2 < N; x2++) for (int y2 = y1; y2 < M; y2++) {
        // cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << ' ' << S(x1, y1, x2, y2) << '\n';
        if(S(x1, y1, x2, y2) <= K) cnt++;
    }
    cout << cnt;
    return 0;
}

修改后

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 6-统计子矩阵-修改后
 * Author:郑龙浩
 * Date:2026-04-08
 * 二维的前缀和
 */
/*
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll nums[30][110] = {0};
int N, M;
ll K;
ll S(int x1, int y1, int x2, int y2) {
    if (x1 == 0 && x2 == 0) return nums[x2][y2];
    if (x1 == 0) return nums[x2][y2] - nums[x2][y1 - 1];
    if (y1 == 0) return nums[x2][y2] - nums[x1 - 1][y2];
    return nums[x2][y2] - nums[x1 - 1][y2] - nums[x2][y1 - 1] + nums[x1 - 1][y1 - 1];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> N >> M >> K;
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) cin >> nums[i][j];
    for (int i = 0; i < N - 1; i++) nums[i + 1][0] += nums[i][0];
    for (int j = 1; j < M; j++) nums[0][j] += nums[0][j - 1];
    for (int i = 1; i < N; i++) for (int j =1; j < M; j++)
        nums[i][j] = nums[i - 1][j] + nums[i][j - 1] + nums[i][j] - nums[i - 1][j - 1];

    int cnt = 0;
    for (int x1 = 0; x1 < N; x1++) for (int y1 = 0; y1 < M; y1++)
    for (int x2 = x1; x2 < N; x2++) for (int y2 = y1; y2 < M; y2++) {
        // cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << ' ' << S(x1, y1, x2, y2) << '\n';
        if(S(x1, y1, x2, y2) <= K) cnt++;
    }
    cout << cnt;
    return 0;
}
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// ll nums[30][110] = {0}; // 错误1:数组开小了,看错题目中的数据了
ll colSum[505][505] = {0};
int N, M;
ll K;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> N >> M >> K;
    // 从1开始存,方便后续计算前缀和,避免出现越界的情况(也不需要专门判断越界的情况了)
    for (int i = 1; i <= N; i++) for (int j = 1; j <= M; j++) cin >> colSum[i][j];

    // 只计算列方向的前缀和,方便矩阵压缩
    for (int i = 2; i <= N; i++) for (int j = 1; j <= M; j++) {
        colSum[i][j] += colSum[i - 1][j];
    }

    ll cnt = 0; // 存储可以满足条件的矩阵的数量
    for (int upper = 1; upper <= N; upper++) {
        for (int lower = upper; lower <= N; lower++) {
            int left = 1; // 窗口左边界
            ll winSum = 0; // 当前窗口之和
            for (int right = left; right <= M; right++) { // 窗口右边界
                winSum += colSum[lower][right] - colSum[upper - 1][right]; // 将第right列的和加到当前窗口中
                while (winSum > K && left <= right) { // 如果当前窗口的和>K,就要将窗口left向右移动
                    winSum -= colSum[lower][left] - colSum[upper - 1][left];
                    left++;
                }
                if (left <= right)
                    cnt += right - left + 1;
            }
        }
    }
    cout << cnt;
    return 0;
}

7-积木画

DP题,我放弃了

问题描述

小明最近迷上了积木画, 有这么两种类型的积木, 分别为 II 型(大小为 2 个单位面积) 和 LL 型 (大小为 3 个单位面积):

同时, 小明有一块面积大小为 2×N2×N 的画布, 画布由 2×N2×N 个 1×11×1 区域构 成。小明需要用以上两种积木将画布拼满, 他想知道总共有多少种不同的方式? 积木可以任意旋转, 且画布的方向固定。

输入格式

输入一个整数 NN,表示画布大小。

输出格式

输出一个整数表示答案。由于答案可能很大,所以输出其对 1000000007 取模后的值。

样例输入

复制代码
3

样例输出

text 复制代码
5

样例说明

五种情况如下图所示,颜色只是为了标识不同的积木:

评测用例规模与约定

对于所有测试用例,1≤N≤100000001≤N≤10000000.

运行限制

  • 最大运行时间:3s
  • 最大运行内存: 512M
cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 7-积木画
 * Author:郑龙浩
 * Date:2026-04-06
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int direction[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {-1, -1}, {1, 1}, {0, -1}, {1, -1}, {-1, 1}};
vector <vector <pair <int, int>>> upNext = {
    {{0, 0}, {0, 1}}, 
    {{0, 0}, {1, 0}}, 
    {{0, 0}, {0, 1}, {1, 0}}, 
    {{0, 0}, {0, 1}, {1, 1}},
    {{0, 0}, {1, 0}, {1, 1}}
};
vector <vector <pair <int, int>>> downNext = {
    {{0, 0}, {-1, 0}},
    {{0, 0}, {0, 1}},
    {{0, 0}, {-1, 0}, {0, 1}},
    {{0, 0}, {0, 1}, {-1, 1}},
    {{0, 0}, {-1, 0}, {-1, 1}}
};

int grid[2][10000000 + 5] = {0};
int N;
bool check() {
    for (int i = 0; i < 2; i++) for (int j = 0; j < N; j++) {
        if (grid[i][j] == 0) return false;
    }
    return true;
}
ll ans = 0;
void dfs(int x, int y) {
    if (check()) {
        ans++;
        return;
    }
    for (int j = 0; j < N; j++) {
        if (grid[0][j] == 1 && grid[1][j] == 1) continue;
        if (grid[0][j] == 0) {
            for (int next = 0; next < 5; next++) {
                vector <pair <int, int>> location;
                bool f = true;
                for (int x = 0; x < upNext[next].size(); x++) {
                    int nextX = 0 + upNext[next][x].first;
                    int nextY = j + upNext[next][x].second;
                    if (nextX < 0 || nextX >= 2 || nextY < 0 || nextY >= N) {f = false; break;}
                    if (grid[nextX][nextY] == 1) {f = false; break;}
                    location.push_back({nextX, nextY});
                }
                if (f) { // 如果这些位置都能填充,就填上
                    for (int i = 0; i < location.size(); i++) {
                        grid[location[i].first][location[i].second] = 1;
                        // dfs();
                        grid[location[i].first][location[i].second] = 0; // 回溯
                    }
                }
            }
        }
        if (grid[1][j] == 0) {
                for (int next = 0; next < 5; next++) {
                vector <pair <int, int>> location;
                bool f = true;
                for (int x = 0; x < downNext[next].size(); x++) {
                    int nextX = 0 + downNext[next][x].first;
                    int nextY = j + downNext[next][x].second;
                    if (nextX < 0 || nextX >= 2 || nextY < 0 || nextY >= N) {f = false; break;}
                    if (grid[nextX][nextY] == 1) {f = false; break;}
                    location.push_back({nextX, nextY});
                }
                if (f) { // 如果这些位置都能填充,就填上
                    for (int i = 0; i < location.size(); i++) grid[location[i].first][location[i].second] = 1;
                }
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> N;
    dfs(0, 0);
    dfs(1, 0);
    
    return 0;
}

8-扫雷

全对

题目描述

在一个 nn 行 mm 列的方格图上有一些位置有地雷,另外一些位置为空。

请为每个空位置标一个整数,表示周围八个相邻的方格中有多少个地雷。

输入描述

输入的第一行包含两个整数 n,mn,m。

第 22 行到第 n+1n+1 行每行包含 mm 个整数,相邻整数之间用一个空格分隔。如果对应的整数为 00,表示这一格没有地雷。如果对应的整数为 11,表示这一格有地雷。

其中,1≤n,m≤1001≤n,m≤100 分钟后还是在当天。

输出描述

输出 nn 行,每行 mm 个整数,相邻整数之间用空格分隔。

对于没有地雷的方格,输出这格周围的地雷数量。对于有地雷的方格,输出 99。

输入输出样例

示例 1

输入

txt 复制代码
3 4
0 1 0 0
1 0 1 0
0 0 1 0

输出

txt 复制代码
2 9 2 1
9 4 9 2
1 3 9 2

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M
cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 8-扫雷
 * Author:郑龙浩
 * Date:2026-04-06
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool grid[105][105] = {false};
int grid2[105][105] = {0};
int n, m;
int direction[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {-1, -1}, {1, 1}, {0, -1}, {1, -1}, {-1, 1}};
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> grid[i][j];
    for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
        if (grid[i][j] == true) {grid2[i][j] = 9; continue;}
        for (int next = 0; next < 8; next ++) {
            int nextX = i + direction[next][0];
            int nextY = j + direction[next][1];
            if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= m) continue;
            if (grid[nextX][nextY] == true) grid2[i][j]++;
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m - 1; j++) cout << grid2[i][j] << ' ';
        cout << grid2[i][m - 1] << '\n';
    }
    return 0;
}

9-李白打酒加强版

10案例通过4个-其余超时

问题描述

话说大诗人李白, 一生好饮。幸好他从不开车。

一天, 他提着酒显, 从家里出来, 酒显中有酒 2 斗。他边走边唱:

无事街上走,提显去打酒。 逢店加一倍, 遇花喝一斗。

这一路上, 他一共遇到店 NN 次, 遇到花 MM 次。已知最后一次遇到的是花, 他正好把酒喝光了。

请你计算李白这一路遇到店和花的顺序, 有多少种不同的可能?

注意: 显里没酒 ( 0 斗) 时遇店是合法的, 加倍后还是没酒; 但是没酒时遇 花是不合法的。

输入格式

第一行包含两个整数 NN 和 MM.

输出格式

输出一个整数表示答案。由于答案可能很大,输出模 1000000007 的结果.

样例输入

复制代码
5 10

样例输出

text 复制代码
14

样例说明

如果我们用 0 代表遇到花,1 代表遇到店,14 种顺序如下:

010101101000000

010110010010000

011000110010000

100010110010000

011001000110000

100011000110000

100100010110000

010110100000100

011001001000100

100011001000100

100100011000100

011010000010100

100100100010100

101000001010100

评测用例规模与约定

对于 40%40% 的评测用例: 1≤N,M≤101≤N,M≤10 。

对于 100%100% 的评测用例: 1≤N,M≤1001≤N,M≤100 。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

10案例通过4个-其余超时

我用的DFS,实际上这个题应该用DP,但是我已经放弃DP的复习了,不想修改了,跳过吧

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 9-李白打酒加强版
 * Author:郑龙浩
 * Date:2026-04-06
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
ll cnt = 0;
ll sum = 2;
void dfs(bool cur) {
    if (n == 0 && m == 0) {
        if (sum == 0 && cur == false) cnt++; // 酒喝光了,并且最后一个是花
        return;
    }
    if (sum <= 0) return; /// 如果店或者花还有,但是酒没了,后面就不用看了,不符合,直接return
    
    if (m > 0) {
        m--; sum--;
        dfs(false);
        m++; sum++;
    }
    if (n > 0) {
        n--; sum *= 2;
        dfs(true);
        n++; sum /= 2;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> n >> m;
    m--; sum--;
    dfs(false);
    m++; sum++;
    n--; sum *= 2;
    dfs(true);
    n++; sum /=2;
    cout << cnt % 1000000007;
    return 0;
}

10-砍竹子

10-砍竹子-拿了5分-运行超时

问题描述

这天, 小明在砍竹子, 他面前有 nn 棵竹子排成一排, 一开始第 ii 棵竹子的 高度为 hihi​.

他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为 HH, 那么

用一次魔法可以 把这一段竹子的高度都变为 ⌊⌊H2⌋+1⌋⌊⌊2H​⌋+1​⌋, 其中 ⌊x⌋⌊x⌋ 表示对 xx 向下取整。小明想 知道他最少使用多少次魔法可

让所有的竹子的高度都变为 1 。

输入格式

第一行为一个正整数 nn, 表示竹子的棵数。

第二行共 nn 个空格分开的正整数 hihi​, 表示每棵竹子的高度。

输出格式

一个整数表示答案。

样例输入

复制代码
6
2 1 4 2 6 7

样例输出

text 复制代码
5

样例说明

其中一种方案:

21426214267→214262→214222→211222→111222→111111​→214262→214222→211222→111222→111111​共需要 5 步完成

评测用例规模与约定

对于 20%20% 的数据, 保证 n≤1000,hi≤106n≤1000,hi​≤106 。 对于 100%100% 的数据, 保证 n≤2×105,hi≤1018n≤2×105,hi​≤1018 。

运行限制

  • 最大运行时间:2s
  • 最大运行内存: 256M

20个案例过了4个-其余案例运行超时

cpp 复制代码
/* 2026-04-05-06-算法打卡day38-蓝桥杯第13届省赛B组C++
 * 10-砍竹子-20个案例过了4个-其余案例运行超时.cpp
 * Author:郑龙浩
 * Date:2026-04-07
 */

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
const int n = 2 * 1e5 + 5;
ll nums[n];
int MaxIndex() {
    int Max = 0;
    for (int i = 1; i < N; i++) {
        if (nums[Max] < nums[i]) Max = i;
    }
    return Max;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> N;
    for (int i = 0; i < N; i++) cin >> nums[i];
    ll cnt = 0;
    
    while (true) {
        int maxIndex = MaxIndex(); // 最大数字索引
        ll MaxNum = nums[maxIndex];
        if (nums[maxIndex] == 1) break;
        int right = maxIndex, left = maxIndex - 1;
        while (right < N && nums[right] == MaxNum) {
            nums[right] = sqrt(nums[right] / 2.0 + 1);
            right++;
        }
        while (left >= 0 && nums[left] == MaxNum) {
            nums[left] = sqrt(nums[left] / 2.0 + 1);
            left--;
        }
        cnt++;
        if (cnt > n) break;
        // cout << maxIndex << ':'; for (int i = 0; i < N; i++) cout << nums[i] << ' '; cout << '\n';
    }
    cout << cnt;
    return 0;
}
相关推荐
美式请加冰2 小时前
最短路径问题
java·数据结构·算法
会编程的土豆2 小时前
【数据结构与算法】 时间复杂度计算
数据结构·c++·算法
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day9】
数据结构·数据库·c++·算法·蓝桥杯
知星小度S2 小时前
算法训练之递归(二)
算法
无限进步_2 小时前
【C++】反转字符串的进阶技巧:每隔k个字符反转k个
java·开发语言·c++·git·算法·github·visual studio
Fly Wine2 小时前
Leetcode只二叉树中序遍历(python解法)
算法·leetcode·职场和发展
bnmoel2 小时前
C语言自定义类型:联合和枚举
c语言·开发语言·数据结构·算法
计算机安禾2 小时前
【数据结构与算法】第34篇:选择排序:简单选择排序与堆排序
c语言·开发语言·数据结构·c++·算法·排序算法·visual studio
汀、人工智能11 小时前
[特殊字符] 第40课:二叉树最大深度
数据结构·算法·数据库架构·图论·bfs·二叉树最大深度