2021年第十二届蓝桥杯省赛B组(C/C++)第二场题解


2021年第十二届蓝桥杯省赛B组(C/C++)第二场题解

1.求余

【问题描述】
在 C/C++/Java/Python 等语言中,使用 % 表示求余,请问 2021%20 的值是多少?

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    cout << 2021 % 20;
    
    return 0;
}

答案:1

2.双阶乘

【问题描述】
一个正整数的双阶乘,表示不超过这个正整数且与它有相同奇偶性的所有正整数乘积。n 的双阶乘用 n!! 表示。
例如:
3!! = 3 × 1 = 3。
8!! = 8 × 6 × 4 × 2 = 384。
11!! = 11 × 9 × 7 × 5 × 3 × 1 = 10395。
请问,2021!! 的最后 5 位(这里指十进制位)是多少?
注意:2021!! = 2021 × 2019 × · · · × 5 × 3 × 1。
提示:建议使用计算机编程解决问题。

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

// 2021!! = 2021 × 2019 × · · · × 5 × 3 × 1。的最后5位
int main()
{
    LL res = 1;
    for(int i = 2021; i >= 1; i -= 2)
        res = res * i % 100000;
    cout << res; 
    
    return 0;
}

答案:59375

3.格点

【题目描述】

如果一个点 (x, y) 的两维坐标都是整数,即 x ∈ Z 且 y ∈ Z,则称这个点为一个格点。
如果一个点 (x, y) 的两维坐标都是正数,即 x > 0 且 y > 0,则称这个点在第一象限。
请问在第一象限的格点中,有多少个点 (x, y) 的两维坐标乘积不超过 2021, 即 x · y ≤ 2021。
提示:建议使用计算机编程解决问题。

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

// 请问在第一象限的格点中,有多少个点 (x, y) 的两维坐标乘积不超过 2021, 即 x · y ≤ 2021。
int main()
{
    int res = 0;
    for(int i = 1; i <= 2040; i ++)
        for(int j = 1; j <= 2040; j ++)
            if(i * j <= 2021)
            {
                res ++;
            }
    cout << res;        
     
    return 0;
}

答案:15698

4.整数分解

【题目描述】

将 3 分解成两个正整数的和,有两种分解方法,分别是 3 = 1 + 2 和3 = 2 + 1。注意顺序不同算不同的方法。
将 5 分解成三个正整数的和,有 6 种分解方法,它们是 1+1+3 = 1+2+2 = 1 + 3 + 1 = 2 + 1 + 2 = 2 + 2 + 1 = 3 + 1 + 1。
请问,将 2021 分解成五个正整数的和,有多少种分解方法?

思路:dp计数问题(完全背包统计方案)

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2030;
long long f[6][N];// f[i][j]:取五个数,和为j的方案数

int main()
{
    for(int j = 1; j <= 2021; j ++) f[1][j] = 1;
    
    for(int i = 2; i <= 5; i ++)// 物品
        for (int j = 1; j <= 2021; j ++ )// 体积
            for(int k = 1; k <= 2021; k ++)// 决策:物品i选择1~2021中的某个数k
            {
                if(j - k >= 0) f[i][j] += f[i - 1][j - k];
            }
    cout << f[5][2021];   
    return 0;
}

5.城邦

【问题描述】
小蓝国是一个水上王国,有 2021 个城邦,依次编号 1 到 2021。在任意两个城邦之间,都有一座桥直接连接。
为了庆祝小蓝国的传统节日,小蓝国政府准备将一部分桥装饰起来。
对于编号为 a 和 b 的两个城邦,它们之间的桥如果要装饰起来,需要的费用如下计算:找到 a 和 b 在十进制下所有不同的数位,将数位上的数字求和。
例如,编号为 2021 和 922 两个城邦之间,千位、百位和个位都不同,将这些数位上的数字加起来是 (2 + 0 + 1) + (0 + 9 + 2) = 14。注意 922 没有千位,千位看成 0。为了节约开支,小蓝国政府准备只装饰 2020 座桥,并且要保证从任意一个城邦到任意另一个城邦之间可以完全只通过装饰的桥到达。
请问,小蓝国政府至少要花多少费用才能完成装饰。
提示:建议使用计算机编程解决问题。

题意:有2021个点,a和b有边权(如题目描述),小蓝国政府准备只装饰 2020 座桥(2021个点有2020条边),并且要保证从任意一个城邦到任意另一个城邦之间可以完全只通过装饰的桥到达(也就是说所有点都是连通的——生成树)。小蓝国政府至少要花多少费用才能完成装饰。

思路:最小生成树模板题——kruskal算法/prim算法

答案:4046

【代码实现】

prim():

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;
const int N = 2030;
//存储方式:邻接矩阵
int g[N][N];//存图:若存在重边,取小者
int dist[N];//储存点到集合(连通块)的最短距离
bool st[N];//连通块s
int n = 2021;

int get(int x, int y)
{
    int res = 0;
    while(x || y)// 注意是或不是与
    {
        int a = x % 10, b = y % 10;
        if(a != b) res += (a + b);
        x /= 10, y /= 10;
    }
    return res;
    
}

int prim()
{
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	
	int res = 0;
	for(int i = 0; i < n; i ++)
	{
		int t = -1;
		for(int j = 1; j <= n; j ++)//找到不在集合中且距离集合最近的点
		{
			if(!st[j] & (t == -1 || dist[t] > dist[j]))
				t = j;
		}
		if(dist[t] == 0x3f3f3f3f) return -1;// 不连通
		
        //更新权重之和;加入集合
		res += dist[t];
		st[t] = true;
		
        //用t更新其它点到 集合s 的距离
		for(int j = 1; j <= n; j ++) dist[j] = min(dist[j], g[t][j]);
	}
	return res;
}

// 请问在第一象限的格点中,有多少个点 (x, y) 的两维坐标乘积不超过 2021, 即 x · y ≤ 2021。
int main()
{
   for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++)
        {
            int c = get(i, j);
            g[i][j] = c;
        }
    cout << prim();    
     
    return 0;
}

kruskal:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2030, M = 1e7 + 10;
int p[N];
int n = 2021, m;

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

int get(int x, int y)
{
    int res = 0;
    while(x || y)
    {
        int a = x % 10, b = y % 10;
        if(a != b) res += (a + b);
        x /= 10, y /= 10;
    }
    return res;
}

int find(int x)  // 并查集
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    // 1. 排序bian
    sort(edge, edge + m);
    
    //2. 枚举bian
    int cnt = 0, res = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edge[i].a, b = edge[i].b, w = edge[i].w;
        a = find(a), b = find(b);
        if(a != b)
        {
            p[a] = b;
            cnt ++;
            res += w;
        }
    }
    return res; // (填空题一定有解)
}

int main()
{
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    for (int i = 1; i <= n; i ++ )
        for (int j = i + 1; j <= n; j ++ )
        {
            int c = get(i, j);
            edge[m ++] = {i, j, c};
        }
    int t = kruskal();
    cout << t;
    
    return 0;
}

6.特殊年份

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CJbdwS81-1646919646632)(E:\picture\1\733.png)]

思路:模拟

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;


//它的千位和十位相等,个位比百位大 1,我们称满足这样条件的年份为特殊年份。
bool is_unique(int year)
{   //1234
    int qian = year / 1000;
    int bai = year / 100 % 10;
    int shi = year / 10 % 10;
    int ge = year % 10;
    
    if(qian == shi && ge - bai == 1) return true;
    else return false;
}


int main()
{
    int m = 5;
    int res = 0;
    while (m -- )
    {
        int year;
        cin >> year;
        if(is_unique(year)) res ++;
    }
    cout << res;
    
    return 0;
}

【代码2】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int m = 5;
    int res = 0;
    while (m -- )
    {
        //1234
        string s;
        cin >> s;
        // 它的千位和十位相等,个位比百位大 1
        if(s[0] == s[2] && (s[3] - s[1]) == 1) res ++;
    }
    cout << res;
    
    return 0;
}

7.小平方

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IwXxGlk6-1646919646633)(E:\picture\1\734.png)]

思路:枚举

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int n;
    cin >> n;
    
    int res = 0;
    for (int i = 1; i <= n - 1; i ++ )
        if(i * i % n * 2 < n )// 注:不能写成 i*i%n < n/2(/是整除而非玩玩完全得到一个数的一半!!)
            res ++;
    cout << res;       
        
    return 0;
}

8.完全平方数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ympnGm78-1646919646634)(E:\picture\1\735.png)]

思路:枚举(部分分数)

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

bool check(LL a, int i)
{
    LL nums = a * i;
    LL x = sqrt(nums);
    if(x * x == nums) return true;
    else return false;
}


int main()
{
    LL a;
    cin >> a;
    for(LL i = 1; i <= 1e12; i ++)
    {
        if(check(a, i)) 
        {
            cout << i;
            return 0;
        }
    }
    
    return 0;
}

思路:
因为每个完全平方数分解质因数后,每个质因数的指数都是偶数所以只要把n分解质因数,判断指数的奇偶性,是奇数的话乘以它的质因数,最终记得答案。

n * x = m^2

给我们一个n要我们求x:意味着m里边质因子的指数都为偶数个,只有这样才能质因子才能分为两份!

n = p1^x1 * p2^x2 * p3^x3 …如果某个质因子的指数是偶数了就不用乘了,如果是奇数,那么该质因子乘上pi(也就是我们求的x)

【代码实现】

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int main()
{
    LL n;
    cin >> n;
    
    LL res = 1;
    for(LL i = 2; i <= n / i; i ++)// 试除法分解质因数
    {
        if(n % i == 0)
        {
            int s = 0;
            while(n % i == 0) s ++, n /= i;
            if(s % 2) res *= i;
        }
    }
    // 还有只一个质因数n(一个:奇数)
    if(n > 1) res *= n;
    cout << res;
    return 0;
}

9.负载均衡

待补

10.国际象棋

待补

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
CSP-S2提高是中国计算机学会(CCF)主办的一项全国性计算机竞赛,旨在挑选出优秀的高中生并为他们提供提高计算机科学和编程能力的平台。2021第二轮题解分为以下几个部分。 第一题是关于石头游戏的思考。题目给出了一堆石头的数量,两位玩家轮流选择石头进行取走,每次可以取走1个或者2个石头,最后无法继续取走者输掉游戏。通过观察可以发现,如果一开始给定的石头数量是3的倍数,那么第一个选手必胜;否则,第一个选手无法必胜。这是因为无论第一个选手怎么选取,第二个选手总可以使得每一轮选取后的石头数量保持在3的倍数。因此,只需要判断起始时石头数量是否为3的倍数即可。 第二题是关于好书的购买。题目给出了若干种书的价格和折扣情况,要求在有限的预算下买到尽可能多的书籍。这是一个经典的背包问题。使用动态规划算法可以解决,按照价格从小到大的顺序遍历书籍,设置一个二维数dp[i][j]表示在前i本书中,花费j的预算能够买到的最多书籍数量。状态转移方程为:dp[i][j]=max(dp[i-1][j], dp[i-1][j-price[i]]+1)。最终的结果即为dp[n][budget],其中n为书籍总数,budget为预算。 第三题是关于均匀生成所有正整数的问题。题目给出了一个区间[L, R],要求输出在该区间内存在的所有正整数。首先通过观察可以发现,对于任意的正整数x,若2x在区间[L, R]内,那么x也在该区间内;若2x+1在区间[L, R]内,那么x也在该区间内。基于这个思路,可以使用递归的方式实现。若L<=R,则输出L,然后递归输出从2*L到R的所有整数。若L>R,则结束递归。 以上就是CSP-S2提高2021第二轮题解的简要概述。希望这些解题思路对参与竞赛的同学有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值