第十一届蓝桥杯第三场软件类省赛 C++ B组

第十一届蓝桥杯第三场软件类省赛 C++ B组

试题 A: 数青蛙

【问题描述】

​ “一只青蛙一张嘴,两只眼睛四条腿。两只青蛙两张嘴,四只眼睛八条腿。三只青蛙三张嘴,六只眼睛十二条腿。……二十只青蛙二十张嘴,四十只眼睛八十条腿。”

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

约定:数字 2 单独出现读成 “两”,在其他数里面读成 “二”,例如 “十二”。10 读作 “十”,11 读作 “十一”,22 读作 “二十二”。请只计算汉字的个数,标点符号不计算。

【解题思路】

​ 1~10是一个汉字;

​ 11~19以及10的倍数是两位;

​ 其余情况三位;

#include <bits/stdc++.h>
using namespace std;
int f(int x){
	if(x <= 10)return 1;
	if(x % 10 == 0)return 2;
	if(x <= 20)return 2;
	return 3;
}
int main(){
	int ans = 200;
	for(int i = 1;i <= 20;i++){
		int a = i;
		int b = i + i;
		int c = i * 4;
		ans += f(a) * 2;
		ans += f(b) + f(c);
	}
	cout << ans << endl;
	return 0;
	//答案 353
}

试题 B: 互质

【问题描述】

​ 今年是 2020 年,今天是 10 月 18 日。
​ 请问在 1 到 2020 中,有多少个数与 1018 互质。

【解题思路】

#include <bits/stdc++.h>
using namespace std;
int main(){
	int ans = 0;
	for(int i = 1;i <= 2020;i++){
		ans += __gcd(i,1018) == 1;
	}
	cout << ans << endl;
	//答案1008
	return 0;
}

试题 C: 车牌

【问题描述】

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

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

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

【解题思路】

​ 总共情况为16*16*16*10*10*10

​ 第一位第二位第三位字符一致,可以看做一位,有16种情况,第四位第五位第六位分别有10种情况

​ 其他位上也类似,减去之后就是答案

#include <bits/stdc++.h>
using namespace std;
int main() {
    int ans = 16 * 16 * 16 * 10 * 10 * 10;
    ans -= 16 * 10 * 10 * 10;
    ans -= 16 * 10 * 10 * 10;
    ans -= 16 * 16 * 10 * 10;
    ans -= 16 * 16 * 16 * 10;
    cout << ans << endl;
    //答案 3997440
    return 0;
}

试题 D: Fibonacci 集合

【问题描述】

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

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

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

  3. 其他元素都不属于 F。

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

【解题思路】

​ 优先队列直到pop出2020个元素

#include <bits/stdc++.h>
using namespace std;
int main() {
    set<int> s;
    priority_queue<int, vector<int>, greater<int>> q;
    q.push(1);q.push(2);q.push(3);q.push(5);q.push(8);
    s.insert(1);s.insert(2);s.insert(3);s.insert(5);s.insert(8);
    int cnt = 0;
    while(q.size()) {
        cnt ++;
        int x = q.top();
        q.pop();
        if(cnt == 2020) {
            cout << x << endl;
            break;
        }
        int w[] = {3 * x + 2, 5 * x + 3, 8 * x + 5};
        for (int i = 0; i < 3; i ++)
            if(!s.count(w[i])) {
                s.insert(w[i]);
                q.push(w[i]);
            }
    }
    //答案: 41269
    return 0;
}

试题 E: 上升子串

【问题描述】

​ 小蓝有一个字母矩阵,他喜欢和小伙伴们在这个矩阵上玩一些游戏。今天,他打算玩找上升子串的游戏。游戏是合作性质的。小蓝和小伙伴们首先要在矩阵中指定一个位置,然后从这个位置开始,向上下左右相邻位置移动,移动必须满足所到达位置上的字母比当前位置大。小蓝和小伙伴们可以移动任意多次,也可以随时停下来,这样就找到了一个上升子串。只要子串在矩阵中的位置不同,就认为是不同的子串。

​ 小蓝想知道,一共可以找到多少个上升子串。小蓝的矩阵很大,已经放在了试题目录下面,叫 inc.txt。为了更清楚的

【解题思路】

​ 记忆化搜索,使用$$f[i][j]$$来表示从i开始到j结束的路径条数,接下来需要dfs来计算即可

#include <iostream>
#include <vector>
using namespace std;
const int maxn = 110;
vector<string> g;
int f[maxn][maxn]; // f[i][j]: 表示以[i,j]开头的上升子序列
int n, m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dfs(int x, int y) { // dfs返回以[x,y]开头的上升子序列
    if(f[x][y]) return f[x][y];
    f[x][y] = 1;
    for(int i = 0; i < 4; i ++ ) {
        int nx = x + dx[i], ny = y + dy[i];
        if(nx >= 0 && nx < n && ny >= 0 && ny < m && g[nx][ny] > g[x][y]) {
            f[x][y] += dfs(nx, ny);
        }
    }
    return f[x][y];
}

int main() {
    freopen("inc.txt", "r", stdin);
    string s;
    while(getline(cin, s)){
    	g.push_back(s);
    }
    n = g.size(), m = g[0].size();
    int res = 0;
    for(int i = 0; i < n; i ++ )
        for(int j = 0; j < m; j ++ ) {
            res += dfs(i, j);
        }

    cout << res << endl;
    return 0;
}

试题 F: 日期识别

【问题描述】

​ 小蓝要处理非常多的数据,其中有一些数据是日期。在小蓝处理的日期中有两种常用的形式:英文形式和数字形式。英文形式采用每个月的英文的前三个字母作为月份标识,后面跟两位数字表示日期,月份标识第一个字母大写,后两个字母小写,日期小于 10 时要补前导 0。1 月到 12 月英文的前三个字母分别是 Jan、Feb、Mar、Apr、May、Jun、Jul、Aug、Sep、Oct、Nov、Dec。数字形式直接用两个整数表达,中间用一个空格分隔,两个整数都不写前导 0。其中月份用 1 至 12 分别表示 1 月到 12 月。

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

【样例输入】

Feb08

【样例输出】

02 08

【解题思路】

​ substr取前三个字符用来判断是几月份即可

#include <bits/stdc++.h>
using namespace std;
int main() {
    string months[13] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    string s;
    cin >> s;
    string month, day;
    month = s.substr(0, 3);
    day = s.substr(3);
    for(int i = 1; i < 13; i ++) {
        if(month == months[i]) cout << i << ' ';
    }
    cout << stoi(day);
    return 0;
}

试题 G: 乘法表

【问题描述】

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

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

​ 请注意,乘法表中两个数相乘的顺序必须为样例中所示的顺序,不能随意
​ 交换两个乘数。
​ 给定 P,请输出 P 进制下的乘法表。

【样例输入】

8

【样例输出】

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

【解题思路】

​ 简单的进制模拟,注意一个十进制转n进制不要写错

#include <bits/stdc++.h>
using namespace std;
char f(int x){
	if(x <= 9 && x >= 0)return x + '0';
	return x - 10 + 'A';
}
int r;
string DecToR(int x){
	string ans = "";
	while(x){
		ans += f(x % r);
		x /= r;
	}
	reverse(ans.begin(),ans.end());
	return ans;
}
int main() {
    cin >> r;
    for(int i = 1;i < r;i++){
    	for(int j = 1;j <= i;j++){
    		cout << DecToR(i) << "*" << DecToR(j) << "=" << DecToR(i * j) << " ";
    	}
    	cout << '\n';
    }
    return 0;
}

试题 H: 限高杆

【问题描述】

​ 某市有 n 个路口,有 m 段道路连接这些路口,组成了该市的公路系统。其中一段道路两端一定连接两个不同的路口。道路中间不会穿过路口。由于各种原因,在一部分道路的中间设置了一些限高杆,有限高杆的路段货车无法通过。在该市有两个重要的市场 A 和 B,分别在路口 1 和 n 附近,货车从市场 A出发,首先走到路口 1 ,然后经过公路系统走到路口 n,才能到达市场 B。

​ 两个市场非常繁华,每天有很多货车往返于两个市场之间。市长发现,由于限高杆很多,导致货车可能需要绕行才能往返于市场之间,这使得货车在公路系统中的行驶路程变长,增加了对公路系统的损耗,增加了能源的消耗,同时还增加了环境污染。市长决定要将两段道路中的限高杆拆除,使得市场 A 和市场 B 之间的路程变短。请问最多能减少多长的距离?

【输入格式】

​ 输入的第一行包含两个整数 n, m,分别表示路口的数量和道路的段数。
​ 接下来 m 行,每行四个整数 a, b, c, d,表示路口 a 和路口 b 之间有一段长度为 c 的道路。如果 d 为 0,表示这段道路上没有限高杆;如果 d 为 1,表示这段道路上有限高杆。两个路口之间可能有多段道路。
输入数据保证在不拆除限高杆的情况下,货车能通过公路系统从路口 1 正常行驶到路口 n。

【输出格式】

​ 输出一行,包含一个整数,表示拆除两段道路的限高杆后,市场 A 和市场B 之间的路程最大减少多长距离。

【样例输入】

5 7
1 2 1 0
2 3 2 1
1 3 9 0
5 3 8 0
4 3 5 1
4 3 9 0
4 5 4 0

【样例输出】

6

【解题思路】

​ 使用spfa来求解此题,但是假设我们枚举所有断点的话,那么很明显会超时,所以需要对其进行改进,拓展dist为二维使用\(dist[n][i]\)来表示从1号点走到n号点,经过限高杆为k个的最短路

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 10010, M = 200010;
int n, m;
bool vis[N][3];
int dist[N][3];
int h[N], e[M], s[M], w[M], ne[M], idx;
void add(int a, int b, int c, int d) {
    e[idx] = b, w[idx] = c, s[idx] = d, ne[idx] = h[a], h[a] = idx ++;
}

void spfa() {
    queue<pii> q;
    memset(dist, 0x3f, sizeof dist);
    q.push({1, 0});
    dist[1][0] = dist[1][1] = dist[1][2] = 0;
    vis[1][0] = true;
    while(q.size()) {
        int u = q.front().first;
        int tcnt = q.front().second;
        q.pop();
        vis[u][tcnt] = false;
        for (int i = h[u]; i != -1; i = ne[i]) {
            int v = e[i];
            int total = tcnt + s[i];
            if(total > 2) continue;
            if(dist[v][total] > dist[u][tcnt] + w[i]) {
                dist[v][total] = dist[u][tcnt] + w[i];
                if(!vis[v][total]) {
                    vis[v][total] = true;
                    q.push({v, total});
                }
            }
        }
    }
}

int main() {
    cin >> n >> m;
    memset(h, -1, sizeof h);
    while(m --) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        add(a, b, c, d), add(b, a, c, d);
    }
    spfa();
    cout << dist[n][0] - min(dist[n][1], dist[n][2]) << endl;
    return 0;
}

试题 I: 画中漂流

【问题描述】

​ 在梦境中,你踏上了一只木筏,在江上漂流。根据对当地的了解,你知道在你下游 D 米处有一个峡谷,如果你向下游前
进大于等于 D 米则必死无疑。现在你打响了急救电话,T 秒后救援队会到达并将你救上岸。水流速度是1 m/s,你现在有 M 点体力。每消耗一点体力,你可以划一秒桨使船向上游前进 1m,否则会向下游前进 1m (水流)。M 点体力需在救援队赶来前花光。因为江面太宽了,凭借你自己的力量不可能上岸。请问,有多少种划桨的方案可以让你得救。

​ 两个划桨方案不同是指:存在某一秒钟,一个方案划桨,另一个方案不划。

【样例输入】

1 6 3

【样例输出】

5

【解题思路】

​ 比较简单的动态规划题,你可以在每一秒中选择划或者不划的两种操作,通过计算剩余的体力和时间可以计算出是否已经落下悬崖,加上两面的合法方案,既可以将答案输出

具体方案为

// dp[i,j] : 花费时间为i,且剩余体力为j的方案数 答案:dp[t][0],初始化f[0][m] = 1;
// 不划f[i - 1][j] , 划f[i - 1][j + 1];
// 关键点:距离可以通过 时间和剩余体力计算出来!
// 初始距离:d, 向上游距离:m - j ,向下流距离 i - (m - j)
#include <bits/stdc++.h>
using namespace std;
const int N = 3010, mod = 1e9 + 7;
int d, t, m;
int dp[N][N];
int main() {
    cin >> d >> t >> m;
    dp[0][m] = 1;
    for(int i = 1; i <= t; i ++ )
        for(int j = 0; j <= m; j ++) {
            int dist = d + (m - j) - (i - (m - j));
            if(dist > 0) {
                dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;// 不划
                dp[i][j] = (dp[i][j] + dp[i - 1][j + 1]) % mod;// 划
            }
        }
    cout << dp[t][0] << endl;
    return 0;
}

试题 J: 旅行家

【问题描述】

​ 从前,在海上有 n 个岛屿,编号 1 至 n。居民们深受洋流困扰,无法到达比自己当前所在岛屿编号更小的岛屿。经过数年以后,岛屿上的人数随着岛屿的编号递增(可能相等)。作为一名出色的旅行家(RP 学家),你想从 1 号岛屿出发开启一次旅程,以获得更多的 RP,因为受到海洋的洋流影响,你只能去到比当前岛屿编号更大的岛屿。因为你比较善良,你会在离开一个岛屿的时候将你的 RP 分散给岛民,具体的:你的 RP 会除以 2(用去尾法取整,或者说向零取整)(当你的 RP 小于零时,岛民也依旧要帮你分担,毕竟你们已经建立起了深厚的友谊)。第 i 号岛屿有 Ti 人, 但是你很挑剔,每次你从 j 号岛屿到达 i 号岛屿时,你只会在到达的岛屿上做 Ti × T j 件好事(一件好事可以获得 1 点 RP)。

​ 唯一不足的是,由于你在岛上住宿,劳民伤财,你会扣除巨量 RP,第 i 号岛屿的住宿扣除 Fi 点 RP。注意:将离开一个岛屿时,先将 RP 扣除一半,再扣除住宿的 RP,最后在新到达的岛屿上做好事,增加 RP。离开 1 号岛屿时需要扣除在 1 号岛屿住宿的 RP,当到达这段旅程的最后一个岛屿上时,要做完好事,行程才能结束,也就是说不用扣除在最后到达的岛屿上住宿的 RP。你因为热爱旅行 (RP),所以从 1 号岛屿开始旅行,初始时你有 0 点 RP。

​ 你希望选择一些岛屿经过,最终选择一个岛屿停下来,求最大的 RP 值是多少?

【样例输入】

3
4 4 5
1 10 3

【样例输出】

19

【解题思路】

​ 类似于spfa的做法,当前点与比他大的所有点都有连边的spfa做法可以较为轻松的获得大多数的分数

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 500010;
int n;
int T[maxn], F[maxn],rp[maxn];
bool st[maxn];
signed main() {
    cin >> n;
    for(int i = 1; i <= n; i ++ ) cin >> T[i];
    for(int i = 1; i <= n; i ++ ) cin >> F[i];
    memset(rp, -0x3f, sizeof rp);
    rp[1] = 0;
    queue<int> q;
    q.push(1);
    st[1] = true;
    while(q.size()) {
        int t = q.front();
        q.pop();
        st[t] = false;
        for(int i = t + 1; i <= n; i ++) {
            if(rp[t] / 2 - F[t] + T[t] * T[i] > rp[i]) {
                rp[i]  =  rp[t] / 2 - F[t] + T[t] * T[i];
                if(!st[i]) {
                    q.push(i);
                    st[i] = true;
                }
            }
        }
    }
    int res = -1e18;
    for(int i = 1; i <= n; i ++) res = max(res, rp[i]);
    printf("%lld\n", res);
    return 0;
}
posted @ 2022-03-31 16:58  0xYuk1  阅读(242)  评论(0)    收藏  举报