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

文章目录

  • 2021年第十二届蓝桥杯省赛B组(C/C++)第二场题解
    • 1.求余
    • 2.双阶乘
    • 3.格点
    • 4.整数分解
    • 5.城邦
    • 6.特殊年份
    • 7.小平方
    • 8.完全平方数
    • 9.负载均衡
    • 10.国际象棋


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

1.求余

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

【代码实现】

#include 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 
#include 

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 
#include 
#include 
#include 

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.国际象棋

待补

你可能感兴趣的:(蓝桥杯历届真题,c++,蓝桥杯,c语言)