第十一届蓝桥杯大赛软件类省赛第二场C/C++大学B组 题目+易懂题解

试题链接:A-J题题面
如果想对着题目一边看题面一边看题解,可以尝试分屏,很方便,快捷键:win+← win+→ 之所以在标题中加入易懂二字是因为博主自己对算法也不怎么懂,我放出来的题解一定是我自己弄懂之后的比较简单的题解,所以是比较适合小白或者初学者看的,如果有任何的疑惑或者建议、意见都可以在评论区回复我
我使用的头文件#include<bits/stdc++.h>是万能头文件 通常写这一个就可以了
题解:
试题A:门牌制作
解题思路:
一个主循环体从1循环到2020
用abcd记录i的每一位的数字
若数字中有2 则res++
用四个if语句的目的是防止漏掉答案
解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int res=0,a,b,c,d;
	for(int i=1;i<=2020;i++)
	{
		a=i/1000;
		b=(i-a*1000)/100;
		c=(i-a*1000-b*100)/10;
		d=i-a*1000-b*100-c*10;
		if(a==2)
			res++;
		if(b==2)
			res++;
		if(c==2)
			res++;
		if(d==2)
			res++;
	}
	cout<<res<<endl;
	return 0;
}
//624

更简洁的代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int res=0;
	for(int i=1;i<=2020;i++)
	{
		int j=i;
		while(j)
		{
			if(j%10==2)
				res++;
			j/=10;
		}
	}
	cout<<res<<endl;
	return 0;
}

试题B:既约分数
解题思路:
两层循环的嵌套,值得一说的是函数__gcd()的使用:
__gcd(x,y)是GNU的内部函数,不是一个标准库里的函数,平时写题直接用这个函数挺方便的,int、long long类型都可以,需要注意的是两个类型必须要相同,还有就是不能用浮点型,当然也可以手写gcd函数,它头文件是algorithm
解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int res=0;
	for(int i=1;i<=2020;i++)
	{
		for(int j=1;j<=2020;j++)
		{
			if(__gcd(i,j)==1)
				res++;
		}
	}
	cout<<res<<endl;
	return 0;
}
//2481215

试题C:蛇形填数
解题思路:
其实这一题在比赛时我并没有写程序而是找规律找出来的,对角线位置的值是1 5 13 每次都是加4的倍数 +4 +8 +12 +16…这样算个几次就到了20 所以比赛时我发现这个规律后就直接算出来了,没有写程序,当然作为学习还是需要写代码的 这种投机取巧只能关键时刻用一用
正经思路:根据题意,找出规律,主要顺着对角线上数字递增的方向看:行数从1开始算,当行数为奇数时,行数值比列数值大1的位置的数值小,即右上角大于左下角,沿着对角线向上递增;当行数为偶数时,行数值比列数值大1的位置数值大,即右上角小于左下角,沿着对角线向下递增;可以观察从(3,3)位置和(4,4)位置开始的对角线。
得出行列变化的规律,顺着这样的规律跟随行列值得变化递增每个位置的值。
i&1的意义是判断i的奇偶性,类似i%2但是用位运算会快很多,听说位运算可以提升60%的效率,要养成多用位运算的习惯
具体实现细节看代码:
解题代码:

#include <bits/stdc++.h>
using namespace std;

int a[50][50];

int main() {
    int num = 40 * 40;
    for(int i = 1, cnt = 1; i <= 40 && cnt <= num; i++) {
        if(i & 1) //偶数行时 
		{
            for(int x = i, y = 1; x >= 1 && y <= i; x--, y++) 
			{
                a[x][y] = cnt++;
            }
        }
        else//奇数行时 
		 {
            for(int x = 1, y = i; x <= i && y >= 1; x++, y--) 
			{
                a[x][y] = cnt++;
            }
        }
    }
    cout<<a[20][20]<<endl;
	return 0;
}
//761

试题D:跑步锻炼
解题思路:直接按题意进行日期的循环,然后判断
解题代码:

#include<bits/stdc++.h>
using namespace std;
int month[13]={0,31,0,31,30,31,30,31,31,30,31,30,31};
int f(int year)
{
	if(year%400==0||(year%4==0&&year%100!=0))//闰年
		month[2]=29;
	else
		month[2]=28; 
}
int main()
{
	int y=2000,m=1,d=1,week=6,ans=2;
	while(y!=2020||m!=10||d!=1)//2020&&10&&1整体取反
	{
		f(y);
		d++;
		week=(week+1)%7;
		if(d>month[m])//一个月轮完 
		{
			d=1;//天数重置 月份加一 
			m++;
			if(m>12)//一年轮完 
			{
				m=1;//月份重置 年份加一 
				y++;
			}
		}
		if(d==1||week==1)
			ans+=2;
		else
			ans++; 
	} 
	cout<<ans<<endl;
	return 0;
}

试题E:七段码
解题思路:实际上就是求连通图的子图数目,涉及到图,并查集和深度搜索相关知识点不了解的可以直接在CSDN上搜索充电,在此就不多加赘述,有很多博主都解释的很通俗易懂
解题代码:

#include <bits/stdc++.h>
using namespace std;

int mp[8][8]; //连通图 

int flag[8]; //标记该点是否被访问过  1表示灯亮着 一共7个灯 1表示亮着 0表示熄的 
int father[8];//把该点放入数组 

int result = 0;

void creat(){
	for (int i = 1; i < 8; i++){
		for (int j = 1; j < 8; j++){
			mp[i][j] = 0;
		}
	}
	mp[1][2] = mp[1][6] = 1;//用数组表示连通图 
	mp[2][1] = mp[2][3] = mp[2][7] = 1;
	mp[3][2] = mp[3][4] = mp[3][7] = 1;
	mp[4][3] = mp[4][5] = 1;
	mp[5][4] = mp[5][6] = mp[5][7] = 1;
	mp[6][1] = mp[6][5] = mp[6][7] = 1;
	mp[7][2] = mp[7][3] = mp[7][5] = mp[7][6] = 1;
}

int find(int i){//查找并查集中的根
	if(father[i] == i) return i;
	return find(father[i]); 
}

void dfs(int i){
	
	if (i == 8){//判断边界 
		for (int m = 1; m <= 7; m++){   
			father[m] = m; //把自己设为自己的父节点 
		}
		for (int i = 1; i <= 7; i++){ //遍历每一种情况 构造并查集 
			for (int j = 1; j <= 7; j++){
				if (mp[i][j] && flag[i] && flag[j]){//check满足 
					//将连通且亮着的灯并在一块 
					int x = find(i);
					int y = find(j);
					if (x != y) {
						father[x] = y;
					} 
				}
			}
		}
		int cnt = 0; //根的个数 
		for (int i = 1; i<= 7; i++){//有几个根就有几个集合 
			if (flag[i] && father[i] == i) cnt++; //亮着的灯的集合数 
		}
		if (cnt == 1) result++;//都连通的话就只有一个结合符合要求 
		return ;
	}
	
	flag[i] = 1;//i灯亮 dfs中i+1亮和灭 标记 
	dfs(i + 1);//继续搜索 
	flag[i] = 0;//i灯灭 
	dfs(i + 1); //这一句不能少,这种情况为该灯熄灭的情况

int main(){
//建图
	creat();
//深搜 
	dfs(1);
	cout << result << endl;
	return 0;
}



试题F:成绩统计
解题思路:很简单不多说,唯一需要注意的就是输出的细节控制,代码中用了两种方法都是可以的
解题代码:

#include<bits/stdc++.h>
using namespace std;
#define max 10010
int main()
{
	int n,a[max],you=0,jige=0;
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	for(int i=0;i<n;i++)
	{
		if(a[i]>=85)
			you++;
		if(a[i]>=60)
			jige++;
	}
	/*double ans1=(double)you/(double)n;
	double ans2=(double)jige/(double)n;
	int res1=int(ans1*1000+5)/10;
	int res2=int(ans2*1000+5)/10;
	printf("%d%\n%d%\n",res2,res1);*/
	printf("%.0f%\n%.0f%%\n",(double)jige*100.0/n,(double)you*100.0/n);
	return 0;
}

试题G:回文日期
解题思路:题意也比较好理解,就是代码稍微有点繁琐
解题代码:

#include <bits/stdc++.h>
using namespace std;

int MONTH[13] = {0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

//判断是否为闰年 
int f(int year)
{
    if (year % 400 == 0 || (year % 4 == 0 && year % 100))
        MONTH[2] = 29;
    else
        MONTH[2] = 28;
}

char s[10];
//判断是否是回文数 
bool check1(int y, int m, int d)
{
    char s1[10];
    s1[0] = y / 1000 % 10 + '0';//八位数分别转化为数字 
    s1[1] = y / 100 % 10 + '0';
    s1[2] = y / 10 % 10 + '0';
    s1[3] = y / 1 % 10 + '0';
    s1[4] = m / 10 % 10 + '0';
    s1[5] = m / 1 % 10 + '0';
    s1[6] = d / 10 % 10 + '0';
    s1[7] = d / 1 % 10 + '0';
    if (s1[0] == s1[7] && s1[1] == s1[6] && s1[2] == s1[5] && s1[3] == s1[4])
        return 1;
    return 0;
}
//判断是否是ABABBABA型的回文数 
bool check2(int y, int m, int d)
{
    char s1[10];
    s1[0] = y / 1000 % 10 + '0';
    s1[1] = y / 100 % 10 + '0';
    s1[2] = y / 10 % 10 + '0';
    s1[3] = y / 1 % 10 + '0';
    s1[4] = m / 10 % 10 + '0';
    s1[5] = m / 1 % 10 + '0';
    s1[6] = d / 10 % 10 + '0';
    s1[7] = d / 1 % 10 + '0';
    if (s1[0] == s1[2] && s1[2] == s1[5] && s1[5] == s1[7] && s1[0] != s1[1] && s1[1] == s1[3] && s1[3] == s1[4] && s1[4] == s1[6])
        return 1;
    return 0;
}
int main()
{
    scanf("%s", s);
    int y, m, d;
    //获取年月日 
    y = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0');
    m = (s[4] - '0') * 10 + s[5] - '0';
    d = (s[6] - '0') * 10 + s[7] - '0';
    int f1 = 0, f2 = 0;

    while (!f1 || !f2)//都找到了的情况下才会停止循环 
    {
        f(y);
        d++;
        if (d > MONTH[m])//一个月轮完 
        {
            d = 1;//天数重置 
            m++;//月份++ 
            if (m > 12)//一年轮完 
            {
                m = 1;//月份重置 
                y++;//年份++ 
            }
        }
        if (check1(y, m, d) && !f1)//!f1保证是第一次找到 
        {
            f1 = 1;
            printf("%04d%02d%02d\n", y, m, d);
        }
        if (check2(y, m, d) && !f2)
        {
            f2 = 1;
            printf("%04d%02d%02d\n", y, m, d);
        }
    }
    return 0;
}

试题H:子串分值和
解题代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 10;

int last[30], n;
char str[N];

int main() {
    scanf("%s", str + 1);
    n = strlen(str + 1);
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        ans += 1ll * (i - last[str[i] - 'a']) * (n - i + 1);
        last[str[i] - 'a'] = i;
    }
    printf("%lld\n", ans);
    return 0;
}

试题I:平面切分
解题代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1005;

int main()
{
	int n;
	scanf("%d", &n);
	int a, b;
	long double A[N], B[N];
	pair<long double, long double> p; 
	set<pair<long double, long double> > s;  //利用set自动去重功能筛选掉重边 
	for(int i = 0; i < n; i++)
	{
		scanf("%d %d", &a, &b);
		p.first = a;
		p.second = b;
		s.insert(p);
	}
	int i = 0;  //将去重后的直线数据放回A,B数组 
	for(set<pair<long double, long double> >::iterator it = s.begin(); it != s.end(); it++, i++)
	{
		A[i] = it -> first;
		B[i] = it -> second;
	}
	long long ans = 2;  //初始情况当只有一条直线时,有两个平面 
	for(int i = 1; i < s.size(); i++)  //从下标1开始,也就是第二条直线 
	{
		set<pair<long double, long double> > pos;  //记录第i条直线与先前的交点 
		for(int j = i-1; j >= 0; j--)
		{
			int a1 = A[i], b1 = B[i];
			int a2 = A[j], b2 = B[j];
			if(a1 == a2)  //遇到平行线无交点,跳出 
				continue; 
			p.first = 1.0*(b2-b1)/(a1-a2);
			p.second = 1.0*a1*((b2-b1)/(a1-a2)) + b1;
			pos.insert(p); 
		}
		ans += pos.size() + 1;  //根据结论,每增加一条直线,对平面数的贡献值是其与先前直线的交点数(不重合)+1 
	} 
	printf("%d\n", ans);
	return 0;
}

试题J:字串排序
解题代码:

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef vector<int> vi;

const int N = 200 + 10, M = 30;
int f[N][M], n;

void get(int& a, int& b) {
    _for(i, 0, N) _rep(j, 1, 26) if (f[i][j] >= n) {
        a = i, b = j;
        return;
    }
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cout.tie(0), cin.tie(0);
    
    memset(f, -0x3f, sizeof f), f[0][0] = 0;
    cin >> n;
    _for(i, 0, N) _rep(j, 1, 26) _for(k, 0, i) 
        f[i][j] = max(f[i][j], f[k][j - 1] + k * (i - k));

    int len = 0, alp = 0, same = 0;
    get(len, alp);

    while (len) {
        if (alp == 1) cout << 'a';
        else if (f[len][alp - 1] >= n)
            alp--, cout << char(alp - 1 + 'a'), n -= len - 1, same = 1;
        else cout << char(alp - 1 + 'a'), n -= len - 1 - same, same++;
        len--;
    }

    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值