蓝桥杯C/C++百校真题赛(2期)全题解(格点、日期格式、车牌、数青蛙、双阶乘、乘法表、Fibonacci集合、排水管道、城邦、画中漂流)

这个练习赛的时间其实早就过了,但是我因为之前要准备期末考就没有写,最近把题补好了,第二期的题目总体来说比第一期水了不少…

Q1 格点

问题描述

如果一个点 ( x , y ) (x, y) (x,y) 的两维坐标都是整数, 即 x ∈ Z x \in \mathbb{Z} xZ y ∈ Z y \in \mathbb{Z} yZ, 则称这个点为 一个格点。

如果一个点 ( x , y ) (x, y) (x,y) 的两维坐标都是正数, 即 x > 0 x>0 x>0 y > 0 y>0 y>0, 则称这个点在 第一象限。

请问在第一象限的格点中, 有多少个点 ( x , y ) (x, y) (x,y) 的两维坐标乘积不超过 2021 , 即 x ⋅ y ≤ 2021 x \cdot y \leq 2021 xy2021

掟示: 建议使用计算机编程解决问题。

答案提交

这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。

运行限制

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

分析:求有序对( i , j ),且 i > 0 , j > 0 , i ∗ j < = 2021 , 显然有 i , j < = 2021 分析: 求有序对(i,j),且i > 0, j > 0, i * j <= 2021, 显然有\\i, j <= 2021 分析:求有序对(ij),且i>0,j>0,ij<=2021,显然有i,j<=2021

/*
* @Author: gorsonpy
* @Date:   2023-01-09 19:54:02
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-09 19:55:12
*/
#include<iostream>
using namespace std;
int main()
{
    int cnt = 0;
    for(int i = 1; i <= 2021; ++i)
        for(int j = 1; j <= 2021; ++j)
            if(i * j <= 2021) ++cnt;
    cout << cnt << endl;
    return 0;
}

Q2 日期格式

问题描述

小蓝要处理非常多的数据, 其中有一些数据是日期。

在小蓝处理的日期中有两种常用的形式: 英文形式和数字形式。

英文形式采用每个月的英文的前三个宁母作为月份标识, 后面跟两位数字 表示日期, 月份标识第一个字母大写, 后两个字母小写, 日期小于 10 时要补 前导 0s 1 月到 12 月英文的前三个字母分别是 Jan、Feb、Mar、Apr、May、 Jun、Jul、Aug、Sep、Oct、Nov、Dec:

数字形式直接用两个整数表达, 中间用一个空格分隔, 两个整数都不写前导 0。其中月份用 1 至 12 分别表示 1 月到 12 月。

输入一个日期的数字形式, 请输出它的英文形式。

输入格式

输入一行包含两个整数, 分别表示日期的月和日。

输出格式

输出对应的英文形式:

样例输入

2 8

 
 
copy

样例输出

Feb08

 
 
copy

样例输入

10 18

 
 
copy

样例输出

Oct18

 
 
copy

运行限制

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

分析:把a映射成对应的英文串,把b按两位输出即可。

/*
* @Author: gorsonpy
* @Date:   2023-01-09 19:55:47
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-09 19:58:13
*/
#include<iostream>
#include<string>
using namespace std;

string m[13] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun"
, "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int main()
{
    int a, b;
    cin >> a >> b;
    cout << m[a];
    printf("%02d", b);
    return 0;
}

Q3 车牌

问题描述

A A A 市的车牌由六位组成, 其中前三位可能为数字 0 至 9 , 或者字母 A \mathrm{A} A F \mathrm{F} F, 每位有 16 种可能。后三位只能是数字 0 至 9。为了减少攀比, 车牌中不能有连 续三位是相同的字符。

例如, 202020 是合法的车牌, AAA202 不是合法的车牌, 因为前三个字 母相同。

请问, A 市有多少个合法的车牌?

分析:直接枚举或许是最快的,排列组合分析的话要分类讨论,应该也不轻松。

/*
* @Author: gorsonpy
* @Date:   2023-01-09 20:06:08
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-09 20:13:08
*/
#include<iostream>
using namespace std;
int main()
{
    int ans = 0;
    for(int a = 0; a <= 15; ++a)
        for(int b = 0; b <= 15; ++b)
            for(int c = 0; c <= 15; ++c)
                for(int e = 0; e <= 9; ++ e)
                    for(int f = 0; f <= 9; ++f)
                        for(int g = 0; g <= 9; ++g)
                            if(!(a == b && b == c || b == c && c == e || c == e && e == f || e == f && f == g))
                                ++ans;
    cout << ans << endl;

}

Q4 数青蛙

问题描述

“一只青蛙一张嘴, 两只眼睛四条腿。两只青蛙两张嘴, 四只眼睛八条腿。 三只青蛙三张嘴, 六只眼睛十二条腿。 ⋯ ⋯ \cdots \cdots 二十只青蛙二卜张嘴, 四十只眼睛八 十条腿。"

请问上面这段文字, 如果完全不省略, 全部写出来, 从 1 到 20 只青蛙, 总 共有多少个汉字。

约定: 数字 2 单独出现读成 “两”, 在其他数里面读成 “二”, 例如 “十二”。 10 读作 “十”, 11 读作 “十一”, 22 读作 “二十二”。

请只计算汉字的个数, 标点符号不计算。

分析:观察到每句话都是十个固定的字 + 4个变量,枚举过程把四个变量计算一下即可。

/*
* @Author: gorsonpy
* @Date:   2023-01-09 20:18:13
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-09 20:31:19
*/
#include<iostream>
using namespace std;

int get(int x)
{
    if(x <= 10) return 1;
    if(x >= 11 && x <= 20 || x % 10 == 0) return 2;
    return 3;
}
int main()
{
    int ans = 0;
    for(int i = 1; i <= 20; ++i)
        ans += 10 + 2 * get(i) + get(2 * i) + get(4 * i);

    cout << ans << endl;
    return 0;
}

Q5 双阶乘

问题描述

一个正整数的双阶乘, 表示不超过这个正整数且与它有相同奇偶性的所有 正整数乘积。 n n n 的双阶乘用 n ! ! n ! ! n!! 表示。

例如:

3 ! ! = 3 × 1 = 3 8 ! ! = 8 × 6 × 4 × 2 = 384 11 ! ! = 11 × 9 × 7 × 5 × 3 × 1 = 10395 \begin{aligned} & 3 ! !=3 \times 1=3 \\ & 8 ! !=8 \times 6 \times 4 \times 2=384 \\ & 11 ! !=11 \times 9 \times 7 \times 5 \times 3 \times 1=10395 \end{aligned} 3!!=3×1=38!!=8×6×4×2=38411!!=11×9×7×5×3×1=10395

请问, 2021 ! ! 2021 ! ! 2021!! 的最后 5 位 (这里指十进制位) 是多少?

注意: 2021 ! ! = 2021 × 2019 × ⋯ × 5 × 3 × 1 2021 ! !=2021 \times 2019 \times \cdots \times 5 \times 3 \times 1 2021!!=2021×2019××5×3×1

提示: 建议使用计算机编程解决问题。

分析:最后五位数字也就是mod 1e5.

/*
* @Author: gorsonpy
* @Date:   2023-01-10 17:20:49
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-10 17:22:05
*/
#include<iostream>
using namespace std;
int main()
{
    int res = 1;
    for(int i = 2021; i >= 1; i -= 2) res = (res * i) % 100000;
    cout << res << endl;
    return 0;
}

Q6 乘法表

问题描述

九九乘法表是学习乘法时必须要掌握的。在不同进制数下,需要不同的乘法表。 例如,四进制下的乘法表如下所示:

1*1=1
2*1=2 2*2=10
3*1=3 3*2=12 3*3=21

 
 
copy

请注意,乘法表中两个数相乘的顺序必须为样例中所示的顺序,不能随意交换两个乘数。

给定 P P P,请输出 P P P 进制下的乘法表。

输入格式

输入一个整数 P P P

输出格式

输出 P P P 进制下的乘法表。 P P P 进制中大于等于 10 10 10 的数字用大写字母 A A A B B B C C C、··· 表示。

样例输入

4

 
 
copy

样例输出

1*1=1
2*1=2 2*2=10
3*1=3 3*2=12 3*3=21

 
 
copy

样例输入

8

 
 
copy

样例输出

1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=11
4*1=4 4*2=10 4*3=14 4*4=20
5*1=5 5*2=12 5*3=17 5*4=24 5*5=31
6*1=6 6*2=14 6*3=22 6*4=30 6*5=36 6*6=44
7*1=7 7*2=16 7*3=25 7*4=34 7*5=43 7*6=52 7*7=61

 
 
copy

评测用例规模与约定

对于所有评测数据, 2 ≤ P ≤ 36 2 ≤ P ≤ 36 2P36

分析:观察到行数为 p − 1 p-1 p1, 每行列数等于该行行号,同时转化进制即可。

/*
* @Author: gorsonpy
* @Date:   2023-01-10 20:12:15
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-10 20:21:24
*/
#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;
unordered_map<int, char> m;
void change(int x, int radix)
{
    int base = radix;
    vector<int> v;
    while(x)
    {
        v.push_back(x % radix);
        x /= radix;
    }
    for(int i = v.size() - 1; i >= 0; --i)
        cout << m[v[i]];
}
int main()
{
    for(int i = 0; i < 10; ++i)
        m[i] = '0' + i;
    for(int i = 10; i <= 35; ++i)
        m[i] = 'A' + i - 10;
    int p;
    cin >> p;

    for(int i = 1; i < p; ++i)
    {
        for(int j = 1; j <= i; ++j)
        {
            int res = i * j;
            change(i, p);
            cout << "*";
            change(j, p);
            cout << "=";
            change(res, p);
            cout << " ";
        }
        cout << endl;
    }

    return 0;
}

Q7 Fibonacci 集合

问题描述

小蓝定义了一个 Fibonacci 集合 F F F, 集合的元素如下定义:

  1. 最小的 5 个 Fibonacci 数 1 , 2 , 3 , 5 , 8 1,2,3,5,8 1,2,3,5,8 属于集合 F F F

  2. 如果一个元素 x x x 属于 F F F, 则 3 x + 2 、 5 x + 3 3 x+2 、 5 x+3 3x+25x+3 8 x + 5 8 x+5 8x+5 都属于集合 F F F

  3. 其他元素都不而于 F F F

请问,这个集合中的第 2020 小元素的值是多少?

分析:从前往后递推,按照类似筛法的方式标记是否出现在集合.

/*
* @Author: gorsonpy
* @Date:   2023-01-10 20:25:46
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-10 20:35:56
*/
#include<iostream>
using namespace std;

const int N = 1e6 + 10;
bool st[N];
int main()
{
    st[1] = st[2] = st[3] = st[5] = st[8] = true;
    int cnt = 0, i = 1;
    while(1)
    {
        if(st[i])
        {
            st[3 * i + 2] = st[5 * i + 3] = st[8 * i + 5] = true;
            ++cnt;
        }
        if(cnt == 2020)
        {
            cout << i;
            break;
        }
        ++i;
    }
    return 0;
}

Q8 排水管道

问题描述

小蓝正在设计一个排水管道的工程,将水从西边一个离地面很高的水库引到东边的地面。

为了项目更有艺术性,小蓝建造了很多高低不同的的柱子用于支撑流水的水槽,柱子之间的间距相同。

当西边的柱子比东边相邻的柱子至少高 1 时,水能正常通过水槽向东流,否则可能出现异常。

但是,小蓝发现施工方建造的柱子是忽高忽低的,不满足西边高东边低的要求。小蓝不得不修改柱子的高度以便水能正常通过水槽。同时,小蓝需要用 上所有的柱子,最东边的柱子高度至少为 1,最西边的柱子高度可以为任意高度。每修改一根柱子的高度,需要花费 1 的代价(不管高度改变多少代价都是 1 1 1)。

请问,小蓝最少花费多大的代价,可以让水能正常通过所有水槽?在修改时,小蓝只会把柱子的高度修改为整数高度。

输入格式

输入的第一行包含一个整数 n n n,表示柱子的数量。

第二行包含 n n n 个整数 h 1 , h 2 , ⋅ ⋅ ⋅ , h n h_1 , h_2 , ···, h_n h1,h2,,hn ,从西向东表示每根柱子的高度。

输出格式

输出一行包含一个整数,表示答案。

样例输入

6
8 9 5 2 1 1

 
 
copy

样例输出

3

 
 
copy

样例说明

至少修改三根柱子,高度变为 8 , 7 , 5 , 3 , 2 , 1 8,7,5,3,2,1 8,7,5,3,2,1

评测用例规模与约定

对于 30 30 30% 的评测用例, 2 ≤ n ≤ 100 , 1 ≤ h i ≤ 100 2 ≤ n ≤ 100,1 ≤ h_i ≤ 100 2n1001hi100。 对于 60 60 60% 的评测用例, 2 ≤ n ≤ 1000 , 1 ≤ h i ≤ 10000 2 ≤ n ≤ 1000,1 ≤ h_i ≤ 10000 2n10001hi10000。 对于所有评测用例, 2 ≤ n ≤ 100000 , 1 ≤ h i ≤ 1 0 9 2 ≤ n ≤ 100000,1 ≤ h_i ≤ 10^9 2n1000001hi109

分析:这是最难的一题。首先对于所有柱子从右边往左编号1 —— n, 因为每个柱子至少要比左边的多1,显然 h i < i h_i < i hi<i的柱子是必然要被修改的。对于除此之外的其他柱子,仍然有可能需要被修改,设 d i = h i − i d_i = h_i - i di=hii,本题的最优解是保留 d i d_i di呈非严格单调上升的最长子序列部分(长度为 l l l),其余柱子做出修改,最后答案为 n − l n-l nl,总体时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn), 下面给出证明:


可行性:首先,把 d i > = 0 的柱子分为两个部分,记作 A , B , 把 d i < 0 的柱 子集合记作 C 。 A 满足 d i 非严格单调上升最长子序列, B 则不。对于 B 和 C 中的柱子需要做出修改,下面给出一种构造方案:将 B 和 C 中的所有 柱子,修改其 h i 使得 d i = d i − 1 . 那么对于整个序列 [ 1 , n ] , 都有 d i > = d i − 1 . 至此 , 整个序列由于 h i = d i + i , 且 i 是严格单调递增的,所以 h i 严格单调递 增 . 最优性:用反证法,假设存在一种比上述更优的解 S ,由于所有可行解都 必须修改 h i < i 的部分 C , S 在 d i > = 0 的部分保留的不是对于 d i 的最长非 严格上升序列,设为 A ′ , 其余为 B ′ ,因为 ∣ A ′ ∣ < ∣ A ∣ , ∣ B ′ ∣ > B ,所以答案不 能比上述更优 . 可行性:首先,把d_i >= 0的柱子分为两个部分,记作A,B,把d_i<0的柱\\子集合记作C。A满足d_i非严格单调上升最长子序列,B则不。对于B\\和C中的柱子需要做出修改,下面给出一种构造方案:将B和C中的所有\\柱子,修改其h_i使得d_i=d_{i-1}.那么对于整个序列[1,n],都有d_i>=d_{i-1}.\\至此,整个序列由于h_i=d_i+i,且i是严格单调递增的,所以h_i严格单调递\\增.\\ 最优性:用反证法,假设存在一种比上述更优的解S,由于所有可行解都\\必须修改h_i<i的部分C,S在d_i >= 0的部分保留的不是对于d_i的最长非\\严格上升序列,设为A',其余为B',因为|A'| < |A|, |B'| > B,所以答案不\\ 能比上述更优.\\ 可行性:首先,把di>=0的柱子分为两个部分,记作AB,di<0的柱子集合记作CA满足di非严格单调上升最长子序列,B则不。对于BC中的柱子需要做出修改,下面给出一种构造方案:将BC中的所有柱子,修改其hi使得di=di1.那么对于整个序列[1,n],都有di>=di1.至此,整个序列由于hi=di+i,i是严格单调递增的,所以hi严格单调递.最优性:用反证法,假设存在一种比上述更优的解S,由于所有可行解都必须修改hi<i的部分CSdi>=0的部分保留的不是对于di的最长非严格上升序列,设为A,其余为B,因为A<A,B>B,所以答案不能比上述更优.


/*
* @Author: gorsonpy
* @Date:   2023-01-11 18:17:00
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-11 19:25:17
*/
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int h[N], f[N], tt;
int main()
{
    int n;
    cin >> n;

    for(int i = n; i; --i) cin >> h[i];

    for(int i = 1; i <= n; ++i)
        h[i] -= i;
  
  f[++tt] = h[1];
    for(int i = 2; i <= n; ++i)
    {
        if(h[i] >= 0)
        {
            if(h[i] >= f[tt]) f[++tt] = h[i];
            else *upper_bound(f + 1, f + tt + 1, h[i]) = h[i];
        }
    }

    cout << n - tt << endl;
    return 0;
}

Q9 城邦

问题描述

小蓝国是一个水上王国, 有 2021 个城邦, 依次编号 1 到 2021。在任意两 个城邦之间, 都有一座桥直接连接。

为了庆祝小蓝国的传统节日, 小蓝国政府准备将一部分桥装饰起来。

对于编号为 a a a b b b 的两个城邦, 它们之间的桥如果要装饰起来, 需要的费 用如下计算: 找到 a a a b b b 在十进制下所有不同的数位, 将数位上的数字求和。

例如, 编号为 2021 和 922 两个城邦之间, 千位、百位和个位都不同, 将这 些数位上的数字加起来是 ( 2 + 0 + 1 ) + ( 0 + 9 + 2 ) = 14 (2+0+1)+(0+9+2)=14 (2+0+1)+(0+9+2)=14 。注意 922 没有千位, 千 位看成 0 。

为了节约开支, 小蓝国政府准备只装饰 2020 座桥, 并且要保证从任意一个 城邦到任意另一个城邦之间可以完全只通过装饰的桥到达。

请问, 小蓝国政府至少要花多少费用才能完成装饰。

提示: 建议使用计算机编程解决问题。

分析:任意两个点连边的边权可按照题意计算,由于2020 = 2021 - 1, 所以所求的是最小生成树。

/*
* @Author: gorsonpy
* @Date:   2023-01-11 20:02:33
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-12 15:32:09
*/
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 2100, M = N * (N - 1) / 2;

int p[N], cnt;
struct edge{
    int a, b, w;
    bool operator<(const edge &W)
    {
        return w < W.w;
    }
}e[M];

int cal(int x, int y)
{
    int res = 0, base = 10;
    for(int i = 0; i < 4; ++i)
    {
        int a = x % base, b = y % base;
        x /= base, y /= base;
        if(a != b) res += a + b;
    }
    return res;
}
void init()
{
    for(int i = 1; i <= 2021; ++i)
        for(int j = i; j <= 2021; ++j)
        {
            int c = cal(i, j);
            e[cnt ++] = {i, j, c};
        }
}
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
int kruscal()
{
    for(int i = 1; i <= 2021; ++i) p[i] = i;
    sort(e, e + cnt);
    
    int res = 0;
    for(int i = 0; i < cnt; ++i)
    {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find(a), b = find(b);
        if(a != b) p[a] = b, res += w;
    }
    return res;
}
int main()
{
    init();
    cout << kruscal() << endl;
    return 0;
}

Q10 画中漂流

问题描述

在梦境中, 你䠖上了一只木筏, 在江上漂流。

根据对当㘿的了解,你知道在你下游 D D D 米处有一个㷋谷, 如果你向下游前进大干等于 D D D 米则必死无疑。

现在你打响了急救电话, T T T 秒后救抜䀒会到达并生你救上岸。水流速度是 1 ∼ m 1 \sim m 1m, 你现在有 M M M 点体力。 每消耕一点体力, 你可以划一秒桨使船向上游前 进 1 ∼ m 1 \sim m 1m, 否则会向下游前进 1 ∼ m 1 \sim m 1m (水流)。 M M M 点体力需在救援队赴来前花光。因为 江面太宽了, 䣸借你自己的力量不可能上岸。

请问, 有多少种划奖的方案可以让你得救。

两个划桨方案不同是指; 存在某一秒铗, 一个方案划桨, 另一个方案不划。

输入格式

输入一行包含三个整数 D , T , M D, T, M D,T,M

输出格式

输出一个整数, 表示可以让你得救的总方案数, 答案可能很大, 请输出方案数除以 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的余数。

样例输入

1 6 3

 
 
copy

样例输出

5

 
 
copy

评测用例规模与约定

对于 50 50 % 50 的评測用例, 1 ≤ T ≤ 350 1 \leq T \leq 350 1T350

对于所有评测用绢, 1 ≤ T ≤ 3000 , 1 ≤ D ≤ T , 1 ≤ M ≤ 1500 1 \leq T \leq 3000,1 \leq D \leq T, 1 \leq M \leq 1500 1T3000,1DT,1M1500

分析:设 f i j f_{ij} fij为第 i i i秒剩余体力为j的方案数目,可以划分为第i秒划或者不划两种集合, f i j = f i − 1 j + f i − 1 j + 1 , 当然状态需要判断是否合法。 f_{ij}=f_{i-1j} + f_{i-1j+1},当然状态需要判断是否合法。 fij=fi1j+fi1j+1,当然状态需要判断是否合法。

/*
* @Author: gorsonpy
* @Date:   2023-01-12 15:49:14
* @Last Modified by:   gorsonpy
* @Last Modified time: 2023-01-12 16:02:02
*/
#include<iostream>
using namespace std;
using LL = long long;
const int N = 3e3 + 10, M = 1510, mod = 1e9 + 7;
LL f[N][M];
int d, t, m;

int main()
{
    cin >> d >> t >> m;

    f[0][m] = 1;
    for(int i = 1; i <= t; ++i)
        for(int j = m; j >= 0; --j)
        {
            int k = d + (m - j) - (i - (m - j));
            if(k > 0)
            {
                f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
                f[i][j] = (f[i][j] + f[i - 1][j + 1]) % mod;
            }
        }
    cout << f[t][0] << endl;
    return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

102101141高孙炜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值