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

写在前面


· 本篇题解对应的是第十三届蓝桥杯省赛C++组的B组的第一场

· 所使用的语言时Python3(其实主要看思路、是什么语言不重要🤫)

· 仅包含8道大题的代码及思路

· 有错误欢迎指出

目录

写在前面

刷题统计 

题目描述

思路及代码

修建灌木 

题目描述

思路及代码 

X进制减法 

题目描述

思路及代码 

统计子矩阵 

题目描述

思路及代码

积木画

题目描述

思路及代码

扫雷

题目描述

思路及代码 

李白打酒加强版

题目描述

思路及代码 

砍竹子 

题目描述

思路及代码 


刷题统计 

题目描述

小明决定从下周一开始努力刷题准备蓝桥杯竞赛。

他计划周一至周五每天做 a 道题目,周六和周日每天做 b 道题目。

请你帮小明计算,按照计划他将在第几天实现做题数大于等于 n 题?

输入格式

输入一行包含三个整数 a,b 和 n。

输出格式

输出一个整数代表天数。

数据范围

对于 50% 的评测用例,1≤a,b,n≤10^6,
对于100% 的评测用例,1≤a,b,n≤10^18

输入样例:

10 20 99

输出样例:

8

思路及代码


# 简单模拟

a,b,n = map(int,input().split())

tot = 5 * a + 2 * b

cnt = n // tot 
n -= tot * cnt
cnt *= 7

flag = 0
while n > 0 :
    cnt += 1
    flag += 1
    if 1 <= flag <= 5 : n -= a
    else : n -= b
    
print(cnt)

修建灌木 

题目描述

爱丽丝要完成一项修剪灌木的工作。
有 N 棵灌木整齐的从左到右排成一排。
爱丽丝在每天傍晚会修剪一棵灌木,让灌木的高度变为 0 厘米。
爱丽丝修剪灌木的顺序是从最左侧的灌木开始,每天向右修剪一棵灌木。
当修剪了最右侧的灌木后,她会调转方向,下一天开始向左修剪灌木。
直到修剪了最左的灌木后再次调转方向。然后如此循环往复。
灌木每天从早上到傍晚会长高 1 厘米,而其余时间不会长高。
在第一天的早晨,所有灌木的高度都是 0 厘米。爱丽丝想知道每棵灌木最高长到多高。

输入格式

一个正整数N ,含义如题面所述。
30%的测试数据:1<N≤10;
100%的测试数据:1<N≤10000。

输出格式

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

输入样例 

3

输出样例 

4
2
4

思路及代码 


# 每个树木被修剪后,爱丽丝有向左和向右走两种情况。我们找两个方向最长的,
# 爱丽丝再次回来*2即可。
n = int(input())

for i in range(1,n+1) :
    print(max(i-1,n-i) * 2)

X进制减法 

题目描述

进制规定了数字在数位上逢几进一。
X 进制是一种很神奇的进制,因为其每一数位的进制并不固定!
例如说某种X 进制数,最低数位为二进制,第二数位为十进制,第三数位为八进制:
则 X 进制数321 转换为十进制数为65。65=3*(2*10)+2*(2)+1*(1)。
现在有两个 X 进制表示的整数 A 和 B,但是其具体每一数位的进制还不确定。
只知道 A 和 B 是同一进制规则,且每一数位最高为 N 进制,最低为二进制。
请你算出 A − B 的结果最小可能是多少。
请注意,你需要保证 A 和 B 在 X 进制下都是合法的,即每一数位上的数字要小于其进制。

输入格式

第一行一个正整数 N,含义如题面所述。
第二行一个正整数 Ma,表示 X 进制数 A 的位数。
第三行 Ma 个用空格分开的整数,表示 X 进制数 A 按从高位到低位顺序各个数位上的数字在十进制下的表示。
第四行一个正整数 Mb,表示 X 进制数 B 的位数。
第五行 Mb 个用空格分开的整数,表示 X 进制数 B 按从高位到低位顺序各个数位上的数字在十进制下的表示。
请注意,输入中的所有数字都是十进制的。
30%的测试数据:2≤N≤10,1≤Ma,Mb≤8。
100%的测试数据:2≤N≤1000,1≤Ma,Mb≤100000,B≤A。

输出格式

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

输入样例

11
3
10 4 0
3
1 2 0

输出样例

94

数据范围与提示

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

思路及代码 

MOD=int(1e9+7)
if __name__ == '__main__':
    n=int(input().strip())
    ma=int(input().strip())
    la=list(map(int,input().strip().split()))
    la.reverse()
    mb=int(input().strip())
    lb=list(map(int,input().strip().split()))
    lb.reverse()

    for i in range(ma-mb):
        lb.append(0)
    cnt=[0]*(ma+1)
    cnt1=[0]*(ma+1)
    for i in range(ma):
        cnt[i]=max(la[i],lb[i],1)+1

    cnt1[0]=1
    for i in range(1,ma):
        cnt1[i]=cnt1[i-1]*cnt[i-1]%MOD
    add1,add2=0,0
    for i in range(ma):
        add1=(add1+cnt1[i]*la[i])%MOD
    for i in range(ma):
        add2=(add2+cnt1[i]*lb[i])%MOD

    ans=(add1-add2)%MOD
    print(ans)


统计子矩阵 

题目描述

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

输入格式

第一行包含三个整数N, M 和K.
之后 N 行每行包含 M 个整数,代表矩阵A.
30%的测试数据:1≤N,M≤20;
70%的测试数据:1≤N,M≤100;
100%的测试数据:1≤N,M≤500;0≤Aij≤1000;1≤K≤250000000。

输出格式

一个整数代表答案。

输入样例

3 4 10
1 2 3 4
5 6 7 8
9 10 11 12

输出样例

19

数据范围与提示

满足条件的子矩阵一共有19,包含:
大小为1 × 1 的有10 个。
大小为1 × 2 的有3 个。
大小为1 × 3 的有2 个。
大小为1 × 4 的有1 个。
大小为2 × 1 的有3

思路及代码


# 时间复杂度 O(n3)
# 这题的思路就是,先枚举两条竖着的直线,为矩阵的左边和右边,然后从上到下双指针扫描,
# 把二维问题变成一维问题:给定一个一维数组,求连续一段区间和小于等于k的区间数量,这可以用双指针去做。
# TLE了两个点。

N,M,K = map(int,input().split())
Map = [[0] * (M+1)]

for i in range(N) :
    Map.append([0] + list(map(int,input().split())))
    
for i in range(1,N+1) :
    for j in range(1,M+1) :
        Map[i][j] += Map[i-1][j] + Map[i][j-1] - Map[i-1][j-1] 
        
ans = 0

for l in range(1,M+1) : # 枚举矩阵的左边
    for r in range(l,M+1) : # 枚举矩阵的右边
        j = 1 
        for i in range(1,N+1) : # 有了左右边界,从上到下双指针扫描
            
            while j <= i and Map[i][r] - Map[j-1][r] - Map[i][l-1] + Map[j-1][l-1] > K : j += 1
            if j <= i : ans += i - j + 1 # 有可能最小的矩阵都不满足,此时j>i

print(ans)

积木画

题目描述

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


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

输入格式

输入一个整数N,表示画布大小。
对于所有测试用例,1 ≤ N ≤ 10000000。

输出格式

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

输入样例 

3

输出样例 

5

思路及代码


'''
dp[i][j]表示前i列填满并且填了第i+1列j个位置的方案,最终答案为dp[n][0]
由于n较大,但第i种状态只与i-1有关,可以用滚动数组优化
'''

mod = 1000000007
dp = [[0] * 3 for i in range(3)] # dp[i][j]表示前i列填满并且填了第i+1列j个位置的方案

dp[1][0] = 1 # 一个长条竖着放
dp[1][1] = 2 # 一个L型竖着放(可以上下翻转所以是两种)
dp[1][2] = 1 # 两个长条横着放

n = int(input())

for i in range(2,n+1) :
    # 这一列长条竖着放 + 前一列占满
    dp[i&1][0] = (dp[i-1&1][0] + dp[i-1&1][2]) % mod
    # 一个L型竖着放(上下翻转*2) + 这一列被占一个再横着放一个长条
    dp[i&1][1] = (dp[i-1&1][0]*2 + dp[i-1&1][1]) % mod
    # 这一列两个长条横着放 + 一个L型摆成7字形
    dp[i&1][2] = (dp[i-1&1][0] + dp[i-1&1][1]) % mod
    
print(dp[n&1][0]%mod)

扫雷

题目描述

小明最近迷上了一款名为《扫雷》的游戏。
其中有一个关卡的任务如下,在一个二维平面上放置着 n 个炸雷
第 i 个炸雷(xi, yi, ri) 表示在坐标(xi, yi) 处存在一个炸雷,它的爆炸范围是以半径为 ri 的一个圆。
为了顺利通过这片土地,需要玩家进行排雷。
玩家可以发射 m 个排雷火箭,小明已经规划好了每个排雷火箭的发射方向。
第 j 个排雷火箭(xj, yj, rj) 表示这个排雷火箭将会在(xj, yj) 处爆炸,它的爆炸范围是以半径为 rj 的一个圆,
在其爆炸范围内的炸雷会被引爆。同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。
现在小明想知道他这次共引爆了几颗炸雷?
你可以把炸雷和排雷火箭都视为平面上的一个点。
一个点处可以存在多个炸雷和排雷火箭。当炸雷位于爆炸范围的边界上时也会被引爆。

输入格式

输入的第一行包含两个整数n、m.
接下来的 n 行,每行三个整数xi, yi, ri,表示一个炸雷的信息。
再接下来的 m 行,每行三个整数xj, yj, rj,表示一个排雷火箭的信息。
40% 的评测用例:0 ≤ x, y ≤ 10^9; 0 ≤ n,m ≤ 10^3; 1 ≤ r ≤ 10:
100% 的评测用例:0 ≤ x, y ≤ 10^9; 0 ≤ n,m ≤ 5 × 10^4; 1 ≤ r ≤ 10:

输出格式

输出一个整数表示答案。

输入样例 

2 1
2 2 4
4 4 2
0 0 5

输出样例 

2

数据范围与提示

示例图如下,排雷火箭1 覆盖了炸雷1,所以炸雷1 被排除;炸雷1 又覆盖了炸雷2,所以炸雷2 也被排除。

思路及代码 

//注:本题需要用到LL数据类型来进行哈希 Python语言较难通过
// 因此这里给大家粘贴一份大佬的C++代码

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 5e4 + 10, M = 1e6 + 7, X = 1e9 + 1;
int n, m;
struct node {
    int x, y, r;
}b[N];
typedef long long ll;
ll h[M]; // 哈希数组
bool st[N]; // 判断是否访问过
int id[M], res; // id数组为哈希数组中key对应的地雷下标

ll get_hs(int x, int y) { // 得到每个坐标的哈希值
    return (ll)x * X + y;
}

int find(int x, int y) { // 找到该坐标被哈希数组存储的下标key
    ll hs = get_hs(x, y);
    int key = (hs % M + M) % M; // 映射到哈希数组内部
    // 如果该位置已经被占用并且不等于我们要求的哈希值,要在之后找到相应位置
    while(h[key] != -1 && h[key] != hs) { 

        key++;
        if(key == M) key = 0; // 哈希表走到末尾,又从0开始
    }
    return key;
}

// 判断x1,y1为圆心,半径为r的圆是否包含x,y
bool check(int x1, int y1, int r, int x, int y) { 
    int d = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);
    return d <= r * r;
}

void bfs(int pos) {
    queue<int> q;
    q.push(pos);
    st[pos] = 1;
    while(q.size()) {
        int t = q.front();
        q.pop();
        int x = b[t].x, y = b[t].y, r = b[t].r;
        for(int xx = x - r; xx <= x + r; xx++) { // 从(x-r, y-r)枚举到(x+r, y+r)
            for(int yy = y - r; yy <= y + r; yy++) {
                int key = find(xx, yy); // 找到该坐标点对应的哈希下标
                // 该点是不是地雷,有没有被访问过,能不能炸到 
                if(id[key] && !st[id[key]] && check(x, y, r, xx, yy)) { 
                    int pos = id[key]; // 获得对应地雷编号
                    st[pos] = 1;
                    q.push(pos);
                }
            }
        }
    }
}

int main() {
    cin >> n >> m;
    memset(h, -1, sizeof(h));
    int x, y, r;
    for(int i = 1; i <= n; i++) { // 读入地雷,存入哈希表
        cin >> x >> y >> r;
        b[i] = {x, y, r};
        int key = find(x, y);// 找到该坐标点对应的哈希下标
        if(h[key] == -1) h[key] = get_hs(x, y); // 如果哈希对应位置为空,则插入

        // id数组没有被标记或者找到了同一个坐标点更大半径的地雷
        if(!id[key] || b[id[key]].r < r) { 
            id[key] = i;
        }
    }
    for(int i = 1; i <= m; i++) { // 枚举导弹
        cin >> x >> y >> r;
        for(int xx = x - r; xx <= x + r; xx++) {
            for(int yy = y - r; yy <= y + r; yy++) {
                int key = find(xx, yy);

                // 如果该点有地雷,没有被访问过,能被炸到
                if(id[key] && !st[id[key]] && check(x, y, r, xx, yy)) bfs(id[key]);
            }
        }
    }
    // 遍历每个地雷,看是否被标记
    for(int i = 1; i <= n; i++) {
        int key = find(b[i].x, b[i].y); // 获得坐标点对应哈希下标
        int pos = id[key]; // 哈希下标对应的地雷编号
        if(pos && st[pos]) res++; // 如果有地雷并且被访问过
    }
    cout << res;
    return 0;
}


李白打酒加强版

题目描述

话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒 2 斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店 N 次,遇到花 M 次。
已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白这一路遇到店和花的顺序,有多少种不同的可能?
注意:壶里没酒( 0 斗) 时遇店是合法的,加倍后还是没酒;但是没酒时遇花是不合法的。

输入格式

输入包含多组测试数据。
第一行为T,表示存在T组测试数据,T不超过30。
对于每组测试数据,输入两个整数N 和M.
1 ≤ N, M ≤ 100。

输出格式

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

输入样例 

1
5 10

输出样例 

14

数据范围与提示

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

010101101000000
010110010010000
011000110010000
100010110010000
011001000110000
100011000110000
100100010110000
010110100000100
011001001000100
100011001000100
100100011000100
011010000010100
100100100010100
101000001010100 

思路及代码 

'''
题目要求的是求遇到遇到花的次数i=N、遇到店的次数j=M、且剩余酒的量k=0斗的情况有多少种。
由此我们的思路就很容易确定了,即设置一个三维数据f[i][j][k],
i即遇到花的次数,j即遇到店的次数,k即当前酒的量。
f[i][j][k]即遇到花的次数为i,遇到店的次数为j,酒量为k的情况数量。
'''

N,M = map(int,input().split())
mod = 1000000007 

dp = [[[0] * (M+5) for i in range(M+5)] for j in range(N+5)]
dp[0][0][2] = 1

for i in range(N+1) : 
    for j in range(M+1) :
        for k in range(M+1) :
            if i and k % 2 == 0 : dp[i][j][k] += dp[i-1][j][k//2] # 当前到店
            if j : dp[i][j][k] += dp[i][j-1][k+1] # 当前到花
            dp[i][j][k] %= mod

# 最后一步必然是到店,所以看倒数第二步的状态
print(dp[N][M-1][1])

砍竹子 

题目描述

这天,小明在砍竹子,他面前有 n 棵竹子排成一排,一开始第 i 棵竹子的高度为 hi.
他觉得一棵一棵砍太慢了,决定使用魔法来砍竹子。
魔法可以对连续的一段相同高度的竹子使用,假设这一段竹子的高度为H,
那么使用一次魔法可以把这一段竹子的高度都变为:

其中 ⌊x⌋ 表示对 x 向下取整。

小明想知道他最少使用多少次魔法可以让所有的竹子的高度都变为1。

输入格式

第一行为一个正整数 n,表示竹子的棵数。
第二行共 n 个空格分开的正整数 hi,表示每棵竹子的高度。
20%的测试数据:n ≤ 1000; hi ≤ 10^6。
100%的测试数据:n ≤ 2 × 10^5; hi ≤ 10^18。

输出格式

一个整数表示答案。

输入样例 

6
2 1 4 2 6 7

输出样例 

5

数据范围与提示

其中一种方案:
2 1 4 2 6 7
→ 2 1 4 2 6 2
→ 2 1 4 2 2 2
→ 2 1 1 2 2 2
→ 1 1 1 2 2 2
→ 1 1 1 1 1 1
共需要 5 步完成。

思路及代码 


'''
这个题本质是一个最长公共下降子序列的问题,
对于任意一个h,只要它高度降到了与前一个高度下降过程中的公共值,那么它就不需要花费代价继续下降。
如果它降得的当前高度与前一个高度没有公共值,则需要多花费一个代价,来降低自己的高度。
我们只需要开两个数组暴力做一下就行。
'''

from math import sqrt
n = int(input())
lst = [0] + list(map(int,input().split()))

def solve(num) :
    return int(sqrt(num//2+1))
    
res = 0
b = [[] for i in range(n+10) ]

for i in range(1,n+1) :
    while lst[i] > 1 :
        flag = 0
        for j in b[i-1] :
            if lst[i] == j :
                flag = 1
                break
        if not flag : res += 1
        b[i].append(lst[i])
        lst[i] = solve(lst[i])
            
print(res)
                

🔚

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UESTC_KS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值