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

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

【传送门】
第六届蓝桥杯大赛个人赛省赛(软件类) C/C++大学B组
第十届蓝桥杯大赛软件类省赛C/C++大学B组
第十一届蓝桥杯大赛软件类省赛第二场C/C++大学B组
第十二届蓝桥杯大赛模拟赛(第四期)
第七届蓝桥杯大赛个人赛省赛(软件类) C/C++大学B组
蓝桥杯基础试题整合C++

  1. 既约分数(最大公约数)
  2. 蛇形走位(二维数组)
  3. 跑步锻炼(闰年,星期)
  4. 七段码(查并集,dfs)
  5. 成绩统计(四舍五入)
  6. 回文日期(回文,枚举)
  7. 子串分值和
  8. 平面切分(欧拉定理)
  9. 字串排序(冒泡排序)

本人的代码思路不是最优解,欢迎一起讨论!!!!
备赛准备,老老实实式代码QVQ

试题B:既约分数
本题总分: 5分
[问题描述]
如果一个分数的分子和分母的最大公约数是1,这个分数称为既约分数。
请问,有多少个既约分数,分子和分母都是1到2020之间的整数(包括1
和2020)?

#include <iostream>
using namespace std;
int fun(int j,int i){
    if(i==0) return j;
    else return fun(i,j%i);//求最大公约数可以用辗转相除法
}

int main()
{
    int a;
    int sum=0;
    for(int i=1;i<=2020;i++){
    for(int j=i+1;j<=2020;j++){
        a=fun(j,i);
        if(a==1){
            sum++;
        }
    }
   }
   sum*=2;//*2是要考虑分母互换还有一种可能
   cout<<++sum<<endl;//最后++是因为在之前的循环中没有考虑到1/1的情况
    return 0;
}

**【运行结果】**2481215
【补充:最小公倍数】

int lcm(int a,int b){
 	return a*b/fun(a,b);//两数之积除最大公倍数
}

试题C:蛇形填数
本题总分: 10 分
[问题描述]
如下图所示,小明用从1开始的正整数“蛇形”填充无限大的矩阵。
1
容易看出矩阵第二行第二列中的数是5。请你计算矩阵中第20行第20列
的数是多少?

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a[40][40];//如果要取到(20,20)那么蛇形走位至少*2的容量
    memset(a,0,sizeof(a));
    int t=1;//t为填充数
    for(int i=0;i<40;i++){
        if(i%2){//如果i是奇数,那么向左下填充
            for(int j=0;j<=i;j++){
                a[j][i-j]=t;
                t++;
            }
        }
        else{//否则像右上填充
              for(int j=0;j<=i;j++){
                a[i-j][j]=t;
                t++;
            }
        }
    }
    cout<<a[19][19]<<endl;
    return 0;
}

【运行结果】761
试题D:跑步锻炼
本题总分: 10分
[问题描述]
小蓝每天都锻炼身体。正常情况下,小蓝每天跑1千米。如果某天是周一或者月初(1日),为了
激励自己,小蓝要跑2千米。如果同时是周一或月初,小蓝也是跑2千米。
小蓝跑步已经坚持了很长时间,从2000年1月1日周六(含)到2020年
10月1日周四(含)。请问这段时间小蓝总共跑步多少千米?

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int day=6;
    int month1[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    int month2[12]={31,29,31,30,31,30,31,31,30,31,30,31};
    int dt=0,flag=0,key=0;//dt距离,flag标记当前月份,key是这月的第几天
    for(int j=2000;j<2020;j++){
        if(j%4==0){//如果是闰年的情况
        for(int i=1;i<=366;i++){
        key++;
        day=day%7;
        if((day==1||key==1)){//判断是否是周一,一号
            dt+=2;
        }
        else{
            dt++;
        }
        if(key==month2[flag]){
            key=0;
            flag++;
        }
        day++;
    }
        flag=0;
    }
    else{
    for(int i=1;i<=365;i++){
        key++;
        day=day%7;
        if((day==1||key==1)){//判断是否是周一,一号
            dt+=2;
        }
        else{
            dt++;
        }
        if(key==month1[flag]){
            key=0;
            flag++;
        }
        day++;
    }
        flag=0;
    }
    }
    for(int i=1;i<=275;i++){//单独处理2020年
        key++;
        day=day%7;
        if((day==1||key==1)){//判断是否是周一,一号
            dt+=2;
        }
        else{
            dt++;
        }
        if(key==month2[flag]){
            key=0;
            flag++;
        }
        day++;
    }
    cout<<dt<<endl;
    return 0;
}

【运行结果】8879

试题E:七段码
本题总分: 15分
[问题描述]
小蓝要用七段码数码管来表示一种特殊的文字。在这里插入图片描述
上图给出了七段码数码管的一个图示,数码管中一共有7段可以发光的二
极管,分别标记为a, b,c, d,e,f, g
小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符
的表达时,要求所有发光的二极管是连成一片的。
例如: b发光,其他二极管不发光可以用来表达一种字符。
例如: c发光,其他二极管不发光可以用来表达一种字符。这种方案与上
一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如: a, b,c, d,e发光,f, g不发光可以用来表达一种字符。
例如: b, f发光,其他二极管不发光则不能用来表达一种字符, 因为发光
的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?

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

int v[8];//一共有7条边
int e[8][8];
int dad[8];//每条边都会有祖先
int ans=0;//记录有几种遍历方法
int find(int n){
    if(dad[n]==n) return n;
    dad[n]=find(dad[n]);
    return dad[n];
}
void dfs(int n){
    if(n>7){//每条边都遍历
        for(int i=1;i<=7;i++){
            dad[i]=i;//首先自己是自己的祖先
        }
        for(int x=1;x<=7;x++)
            for(int y=1;y<=7;y++)
                if(e[x][y]&&v[x]&&v[y]){//如果是遍历的顶点并且有边存在
                    int root_x=find(x);
                    int root_y=find(y);
                    if(root_x!=root_y){
                        dad[root_x]=root_y;//典型的差并集
                    }
                }
        int t=0;
        for(int i=1;i<=7;i++)
            if(v[i]&&dad[i]==i)
            t++;
            if(t==1)
            ans++;
            return;
        }
    v[n]=1;
    dfs(n+1);//从这个边寻找
    v[n]=0;
    dfs(n+1);//回溯换新边
}

int main()
{
    memset(v,0,sizeof(v));
    memset(e,0,sizeof(e));
    memset(dad,0,sizeof(dad));
    //把有关系的两条边标记{a,b,c,d,e,f,g}={1,2,3,4,5,6,7}
    e[1][2]=e[2][1]=1;
    e[1][6]=e[6][1]=1;
    e[2][7]=e[7][2]=1;
    e[2][3]=e[3][2]=1;
    e[3][4]=e[4][3]=1;
    e[3][7]=e[7][3]=1;
    e[4][5]=e[5][4]=1;
    e[5][7]=e[7][5]=1;
    e[5][6]=e[6][5]=1;
    e[6][7]=e[7][6]=1;
    dfs(1);//从1号边(a)开始深度优先搜索
    cout<<ans<<endl;
    return 0;
}

【运行结果】80(相似题 历届试题 国王的烦恼)

试题F:成绩统计
时间限制: 1.0s内 存限制: 256.0MB本题总分: 15 分
[问题描述]
小蓝给学生们组织了一场考试,卷面总分为100分,每个学生的得分都是
一个0到100的整数。
如果得分至少是60分,则称为及格。如果得分至少为85分,则称为优秀。.
请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整
数。
[输入格式]
输入的第一行包含一个整数n,表示考试人数。
接下来n行,每行包含一个0至100的整数,表示一个学生的得分。
[输出格式]
输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分四舍五入保留整数。

#include <bits/stdc++.h>
using namespace std;
int num1=0,num2=0;//及格人数//优秀人数
int main()
{
    int a,n;
    int p1,p2;
    cin>>n;//考试人数
    for(int i=0;i<n;i++){
        cin>>a;//考试分数
        if(a>=60){
            num1++;
            if(a>=85){
                num2++;
            }
        }
    }
    p1=((num1+0.000)/n+0.005)*100;
    p2=((num2+0.000)/n+0.005)*100;//四舍五入(+0.005),百分比(*100)
    cout<<p1<<"%"<<endl;
    cout<<p2<<"%"<<endl;
    return 0;
}

在这里插入图片描述
试题G:回文日期
时间限制: 1.0s内存限制: 256.0MB本题总分: 20分
[问题描述]
2020年春节期间,有一个特殊的日期引起了大家的注意: 2020年2月2日。因为如果将这个日期按"yyymmdd"的格式写成一个8位数是20200202,恰好是一个回文数。我们称这样的日期是回文日期。有人表示20200202是“千年一遇” 的特殊日子。对此小明很不认同,因为不到2年之后就是下一个回文日期: 20211202 即2021年12月2日。
也有人表示20200202并不仅仅是一个回文日期,还是一个ABABBABA型的回文日期。对此小明也不认同,因为大约100年后就能遇到下一个ABABBABA型的回文日期: 21211212 即2121 年12月12日。算不上“千
年-一遇",顶多算“千年两遇”。给定一个8位数的日期,请你计算该日期之后下一个回文日期和下一个
ABABBABA型的回文日期各是哪一天。
[输入格式]
输入包含一个八位整数N,表示日期。
[输出格式]
输出两行,每行1个八位数。第一行表示下一个回文日期,第二行表示下
一个ABABBABA型的回文日期。

#include <iostream>
#include <sstream>
#include <string>
using namespace std;
//思路:把日期分为两部分,年份/日期
//普通回文,改变年份+1,判断有没有符合的日期
//特殊回文,判断有没有月 日相等的日期
int month1[12]={31,28,31,30,31,30,31,31,30,31,30,31};
int month2[12]={31,29,31,30,31,30,31,31,30,31,30,31};

inline string toString(int a){//后面输出时需要逆序,转字符串会简单一点
    ostringstream os;
    os<<a;
    return os.str();
}
bool year(int n){//判断是否是闰年
    if(n%100==0){
        if(n%400==0){
            return true;
        }
        return false;
    }
    else{
        if(n%4==0){
            return true;
        }
        return false;
    }
}
int main()
{
    int s1,s2;
    int k=0;
    string str1,str2;
    cin>>s1;
    s2=s1/10000;
    int num[10000];
    int flag_1=1;//标记是否找到,找到退出循环
    int flag_2=1;
    //有一种特殊的情况就是前半部分回文大于后半部分
    int y_month=s2%10*10+(s2%100)/10;
    int y_day=(s2/100)%10*10+s2/1000;
    int month=s1%10000/1000*10+s1%1000/100;
    int day=s1%100/10*10+s1%10;
    if(y_month>month||(y_month==month&&y_day>day)){
        if(s2){
            if(y_month<=12&&month2[month-1]>y_day){
            num[k++]=s2;
            flag_1=0;
            if(y_month==y_day){
            flag_2=0;
            str1=toString(num[0]);
            str2=toString(s2);
            cout<<s2<<str2[3]<<str2[2]<<str2[1]<<str2[0]<<endl;
            cout<<s2<<str2[3]<<str2[2]<<str2[1]<<str2[0]<<endl;
                }
            }
        }
        else{
            if(y_month<=12&&month1[month-1]>y_day){
            num[k++]=s2;
            flag_1=0;
            if(y_month==y_day){
            flag_2=0;
            str1=toString(num[0]);
            str2=toString(s2);
            cout<<s2<<str2[3]<<str2[2]<<str2[1]<<str2[0]<<endl;
            cout<<s2<<str2[3]<<str2[2]<<str2[1]<<str2[0]<<endl;
            }
        }
    }
    }
    while(flag_1||flag_2){
        for(int i=s2+1;i<=9999;i++){//暴力枚举,感觉也没有多大
            month=i%10*10+(i%100)/10;//提出月 日
            day=(i/100)%10*10+i/1000;
            bool f=year(i);
            if(f){//是闰年
                if(month<=12&&month2[month-1]>day){
                    num[k++]=i;
                    flag_1=0;
                    int a1,a2,b1,b2;
                    a1=month/10;
                    b1=month%10;
                    a2=day/10;
                    b2=day%10;
                    if(a1==a2&&b1==b2){
                    flag_2=0;
                    str1=toString(num[0]);
                    str2=toString(i);
                    cout<<num[0]<<str1[3]<<str1[2]<<str1[1]<<str1[0]<<endl;
                    cout<<i<<str2[3]<<str2[2]<<str2[1]<<str2[0]<<endl;
                    break;
                    }
                }
            }
            else{
                if(month<=12&&month1[month-1]>day){
                    num[k++]=i;
                    flag_1=0;
                    int a1,a2,b1,b2;
                    a1=month/10;
                    b1=month%10;
                    a2=day/10;
                    b2=day%10;
                    if(a1==a2&&b1==b2){
                    flag_2=0;
                    str1=toString(num[0]);
                    str2=toString(i);
                    cout<<num[0]<<str1[3]<<str1[2]<<str1[1]<<str1[0]<<endl;
                    cout<<i<<str2[3]<<str2[2]<<str2[1]<<str2[0]<<endl;
                    break;
                    }
                }
            }
        }
    }
    return 0;
}

在这里插入图片描述
试题H:子串分值和

时间限制: 1.0s内存限制: 256.0MB本题总分: 20分
[问题描述]
对于一个字符串s,我们定义s的分值f(S)为S中出现的不同的字符个数。例如f(“aba”)=2, f(“abc”)=3, f(“aaa”)= 1.现在给定一个字符串S[0…n-1] (长度为n),请你计算对于所有S的非空子串s【i…j】0≤i≤j<n), f(s[i.j)的和是多少。
[输入格式]
输入一行包含一个由小写字母组成的字符串s.
[输出格式]
输出一个整数表示答案。.
【提供一下思路,但是运行超时(毕竟双重枚举,但是我觉得这个代码是很容易明白的),如果有什么空间换时间的方法,或者不用枚举的办法欢迎指出讨论】

#include <iostream>
#include <string>
#include <map>//想到map容器不允许有重复出现感觉很适合这道题
                //map的统计也很方便
//p.s:为什么不用set容器?set空间换时间效果没有map好
using namespace std;

int main()
{
    string s;
    cin>>s;
    long long int sum=0;
    map<char,int> m;
    for(int i=0;i<s.length();i++){
        for(int j=0;(i+j)<s.length();j++){
            m.insert(pair<char,int>(s[i+j],0));//pair要成对出现,后面的0只是凑数,无意义
            sum+=m.size();
        }
        m.clear();//清空操作
    }
    cout<<sum<<endl;
    return 0;
}

在这里插入图片描述

【正解】
参考代码

#include <bits/stdc++.h>//万能头文件
using namespace std;
//大佬普遍做法都是贡献值,
//【简单来说,就是每个字母都有贡献值,如果失去它也就失去它的贡献值,要减去
//如果后面这个字母重复出现了,那么后出现要重新计算贡献值,之前的终止贡献】
typedef long long ll;
const int N = 1e6 + 10;
char s[N];
ll vis[40];
int main() {             
  scanf("%s", s + 1);
  int n = strlen(s + 1);
  ll ans = 0;
  for (int i = 1; i <= n; i++) {
    ans += (i - vis[s[i] - 'a']) * (n - i + 1);
    //ans+=这个字母与上次出现的距离(贡献值)*贡献长度
    vis[s[i] - 'a'] = i; //更新该字符出现位置 
  }
  cout << ans << endl;
  return 0;
}

在这里插入图片描述
试题I:平面切分
时间限制: 1.0s内 存限制: 256.0MB本题总分: 25分
[问题描述]
平面上有N条直线,其中第i条直线是y=Ai * x+ Bi
请计算这些直线将平面分成了几个部分。
[输入格式]
第一行包含一个整数N.
以下N行,每行包含两个整数Ai, Bi。
[输出格式]
一个整数代表答案。

#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;
}

【代码是大佬写的,每增加交点增加平面数真的好赞!原代码指路】

版权声明:本文为CSDN博主「江南路一号」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ucler/article/details/114981570
在这里插入图片描述

【思路图解】
在这里插入图片描述
试题J:字串排序
时间限制: 1.0s内 存限制: 256.0MB本题总分: 25分
[问题描述]
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符, 则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。例如,对于字符串lan排序,只需要1次交换。对于字符串qiao排序, 总共需要4次交换。小蓝的幸运数字是V,他想找到一个只包含小写英文字母的字符串,对这个串中的字符进行冒泡排序,正好需要V次交换。请帮助小蓝找一个这样的字符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
[输入格式]
输入一行包含一个整数V,为小蓝的幸运数字。
[输出格式]
输出一个字符串,为所求的答案。

/既然要字符串最短,那么比较次数就要最多(逆序,但是只有26个字母只完成30%//在最坏的情况下,交换的次数是n*(n-1)/2我们就可以算出字符串长度
//根据溢出的交换次数,调整
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值