第十一届蓝桥杯大赛软件类省赛第二场C/C++大学B组
【传送门】
第六届蓝桥杯大赛个人赛省赛(软件类) C/C++大学B组
第十届蓝桥杯大赛软件类省赛C/C++大学B组
第十一届蓝桥杯大赛软件类省赛第二场C/C++大学B组
第十二届蓝桥杯大赛模拟赛(第四期)
第七届蓝桥杯大赛个人赛省赛(软件类) C/C++大学B组
蓝桥杯基础试题整合C++
- 既约分数(最大公约数)
- 蛇形走位(二维数组)
- 跑步锻炼(闰年,星期)
- 七段码(查并集,dfs)
- 成绩统计(四舍五入)
- 回文日期(回文,枚举)
- 子串分值和
- 平面切分(欧拉定理)
- 字串排序(冒泡排序)
本人的代码思路不是最优解,欢迎一起讨论!!!!
备赛准备,老老实实式代码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开始的正整数“蛇形”填充无限大的矩阵。
容易看出矩阵第二行第二列中的数是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我们就可以算出字符串长度
//根据溢出的交换次数,调整