C++省赛B组个人题解(部分)
题目
【问题描述】小蓝要为一条街的住户制作门牌号。这条街一共有2020位住户,门牌号从1到2020编号。小蓝制作门牌的方法是先制作0到9这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌1017需要依次粘贴字符1、0、1、7,即需要1个字符0,2个字符1,1个字符7。请问要制作所有的1到2020号门牌,总共需要多少个字符2?
答案:624
分析
打卡题
#include
using namespace std;
int ans=0;
void f(int n)
{
while(n)
{
if(n%10==2)
ans++;
n/=10;
}
}
int main()
{
for(int i=1;i<=2020;i++)
f(i);
cout<<ans;
return 0;
}
题目
问题描述】如果一个分数的分子和分母的最大公约数是1,这个分数称为既约分数。例如,3/4,5/2,1/8,7/1都是既约分数。请问,有多少个既约分数,分子和分母都是1到2020之间的整数(包括1和2020)?
【答案提交】这是一道结果填空题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:2481215
分析
求gcd
#include
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){
return a%b==0?b:gcd(b,a%b);
}
int main()
{
int ans=0;
for(ll i=1;i<=2020;i++)
for(ll j=1;j<=2020;j++)
{
if(gcd(i,j)==1)
ans++;
}
cout<<ans;
return 0;
}
题目
【问题描述】
如下图所示,小明用从1开始的正整数“蛇形”填充无限大的矩阵。容易看出矩阵第二行第二列中的数是5。请你计算矩阵中第20行第20列的数是多少?
答案:761
分析
在考场上没多想,只想到了暴力模拟法。。。斜着看就是奇数行从左到右,偶数行从右到左。不过现在仔细观察,题目求得是20行20列,好像直接公式2*n^2 - 2 * n + 1直接解出来了
#include
using namespace std;
int main()
{
int a[105][105],i,j,cnt=1;
for(i=1;i<=100;i++)//分为奇数行和偶数行讨论
{
if(i%2)//奇数行 从左到右
{
for(j=1;j<=i;j++)
a[j][i+1-j]=cnt++;
}
else//偶数行 从右到左
{
for(j=i;j>=1;j--)
a[j][i+1-j]=cnt++;
}
}
cout<<a[20][20];
return 0;
}
题目
【问题描述】小蓝每天都锻炼身体。正常情况下,小蓝每天跑1千米。如果某天是周一或者月初(1日),为了激励自己,小蓝要跑2千米。如果同时是周一或月初,小蓝也是跑2千米。小蓝跑步已经坚持了很长时间,从2000年1月1日周六(含)到2020年10月1日周四(含)。请问这段时间小蓝总共跑步多少千米?
答案:8879
分析
求日期,考场上可以打开计算器调到日期模式,计算两个日期的差值,当时直接一天天的加做出来的
#include
using namespace std;
int a[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
}; //数组记录12个月的天数 a[0][m]对应平年,a[1][m]对应闰年
int leapyear(int y)//判断闰年
{
return y%4==0&&y%100!=0||y%400==0;
}
int days(int y,int m)//返回y年m月的天数
{
return a[leapyear(y)][m];
}
int main()
{
int y=2000,m=1,d=1,week=6,cnt=0;
bool flag=1;
while(flag)
{
if(y==2020&&m==10&&d==1)
flag=0;
cnt++;//跑一千米
if(d==1||week==1)
cnt++;//额外跑一千米
//日期处理
week=week==7?1:(week+1);
d++;
if(d>days(y,m))
{
d=1;
m++;
}
if(m>12)
{
m=1;
y++;
}
}
cout<<cnt;
return 0;
}
题目
【问题描述】
小蓝要用七段码数码管来表示一种特殊的文字。
七段码上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二极管,分别标记为 a, b, c, d, e, f, g。小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?
emm图片大概长这个样子
答案:80
分析
考场上想复杂了,当时想着求连通块再计数,最后没做出来,非常可惜。其实直接枚举,然后排除不符合条件的即可。
a的邻居:f,b ;
b的邻居:a,g,c
c的邻居:b,g,d ;
d的邻居:e,c
e的邻居:d,f ;
f的邻居:a,g,e
g的邻居:b,c,e,f
然而然而,要排除3种,abed,afcd,bcef
早知道一开始就自己数数了,反正也就2^7 哈哈哈
#include
using namespace std;
int a,b,c,d,e,f,g,vis[7],ans=0;
int check()
{
int cnt=0;
for(int i=0;i<7;i++)
cnt+=vis[i];
if(!cnt) return 0;
else if(cnt==1||cnt==7) return 1;
else //有2~6根管子,判断他们的邻居是不是都没亮
{
if(a)
{
if(!b&&!f) return 0;
}
if(b)
{
if(!a&&!g&&!c) return 0;
}
if(c)
{
if(!b&&!g&&!d) return 0;
}
if(d)
{
if(!e&&!c) return 0;
}
if(e)
{
if(!g&&!d&&!f) return 0;
}
if(f)
{
if(!a&&!g&&!e) return 0;
}
if(g)
{
if(!b&&!c&&!e&&!f) return 0;
}
return 1;
}
}
int main()
{//七重循环
for(a=0;a<2;a++)
for(b=0;b<2;b++)
for(c=0;c<2;c++)
for(d=0;d<2;d++)
for(e=0;e<2;e++)
for(f=0;f<2;f++)
for(g=0;g<2;g++){
vis[0]=a;vis[1]=b;vis[2]=c;vis[3]=d;
vis[4]=e;vis[5]=f;vis[6]=g;
ans+=check();
}
cout<<ans-3;
return 0;
}
题目
【问题描述】
小蓝给学生们组织了一场考试,卷面总分为100分,每个学生的得分都是一个0到100的整数。如果得分至少是60分,则称为及格。如果得分至少为85分,则称为优秀。请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整数。
【输入格式】
输入的第一行包含一个整数n,表示考试人数。接下来n行,每行包含一个0至100的整数,表示一个学生的得分。
【输出格式】
输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分四舍五入保留整数。
【样例输入】
7
80
92
56
74
88
100
0
【样例输出】
71%
43%
【评测用例规模与约定】
对于50%的评测用例,1<=n<=100。对于所有评测用例,1<=n<=10000。
分析
四舍五入操作,打卡题
#include
using namespace std;
int main()
{
int n,a[10005],i,cnt1=0,cnt2=0;
cin>>n;
for(i=0;i<n;i++)
{
cin>>a[i];
if(a[i]>=60) cnt1++;
if(a[i]>=85) cnt2++;
}
double ans1=cnt1*1.0/(n*1.0);
double ans2=cnt2*1.0/(n*1.0);
cout<<(int)((ans1+0.005)*100)<<"%"<<endl;
cout<<(int)((ans2+0.005)*100)<<"%"<<endl;
return 0;
}
题目
【问题描述】
2020年春节期间,有一个特殊的日期引起了大家的注意:2020年2月2日。因为如果将这个日期按“yyyymmdd”的格式写成一个8位数是20200202,恰好是一个回文数。我们称这样的日期是回文日期。有人表示20200202是“千年一遇”的特殊日子。对此小明很不认同,因为不到2年之后就是下一个回文日期:20211202即2021年12月2日。也有人表示20200202并不仅仅是一个回文日期,还是一个ABABBABA型的回文日期。对此小明也不认同,因为大约100年后就能遇到下一个ABABBABA型的回文日期:21211212即2121年12月12日。算不上“千年一遇”,顶多算“千年两遇”。给定一个8位数的日期,请你计算该日期之后下一个回文日期和下一个ABABBABA型的回文日期各是哪一天。
【输入格式】
输入包含一个八位整数N,表示日期。
【输出格式】
输出两行,每行1个八位数。第一行表示下一个回文日期,第二行表示下一个ABABBABA型的回文日期。
【样例输入】
20200202
【样例输出】
20211202
21211212
【评测用例规模与约定】
对于所有评测用例,10000101 <= N <= 89991231,保证N是一个合法日期的8位数表示。
分析
和D题一样,模拟日期,注意输出格式。
#include
using namespace std;
int a[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
}; //数组记录12个月的天数 a[0][m]对应平年,a[1][m]对应闰年
int leapyear(int y)//判断闰年
{
return y%4==0&&y%100!=0||y%400==0;
}
int days(int y,int m)//返回y年m月的天数
{
return a[leapyear(y)][m];
}
bool check1(int n) //回文
{
int sum=0,k=n;
while(n)
{
sum*=10;
sum+=n%10;
n/=10;
}
return sum==k;
}
bool check2(int n)//ababbaba回文
{
int w[8],k=7;
while(n)
{
w[k--]=n%10;
n/=10;
}
if(w[0]!=w[2]||w[0]!=w[5]||w[0]!=w[7]||w[0]==w[1])
return 0;
if(w[1]!=w[3]||w[1]!=w[4]||w[1]!=w[6])
return 0;
return 1;
}
int main()
{
int n,y,m,d;
int y1,m1,d1,y2,m2,d2;
bool flag1=1,flag2=1;
cin>>n;
y=n/10000,m=n/100%100,d=n%100;
while(flag1||flag2)
{
//日期处理
d++;
if(d>days(y,m))
{
d=1;
m++;
}
if(m>12)
{
m=1;
y++;
}
n=y*10000+m*100+d;
if(check1(n)&&flag1)
{
y1=y;m1=m;d1=d;
flag1=0;
}
if(check2(n)&&flag2)
{
y2=y;m2=m;d2=d;
flag2=0;
}
}
printf("%04d%02d%02d\n",y1,m1,d1);
printf("%04d%02d%02d\n",y2,m2,d2);
return 0;
}
分析
考试的时候看到样例就想到直接求子串,然后针对每个子串求f(s)值相加,超时。正确做法为遍历字符串每一个字符,记录当前字符在之前出现的位置,乘起来即为答案。
个人做法(超时):
#include
using namespace std;
map<string,int> m;
int f(string s)
{
if(!m[s])//如果子串没有出现过
{
set<char>ss;
for(int i=0;i<s.length();i++)
ss.insert(s[i]);
m[s]=ss.size();
}
return m[s];
}
int main()
{
int i,j,ans=0;
string s;
cin>>s;
for(i=0;i<s.length();i++)
for(j=1;j<=s.length()-i;j++)
{
ans+=f(s.substr(i,j));
}
cout<<ans;
return 0;
}
正解:对于字符串,我们记录每一个字符第一次出现的位置,例如字符串"ababc":第一个字符a出现位置为-1(初始化为),因此对于这个字符,前面可以选择的字符数量为1,后面可以选择的字符数量为n-i,两者相乘即为这个字符贡献的子串分值
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int pre[maxn], last[maxn];
int main()
{
ll ans = 0;
string s;
cin>>s;
int n = s.size();
for (int i = 0; i < 26;i++) //初始化为-1 方便计算
last[i] = -1;
for (int i = 0; i < n;i++)
{
int x = s[i] - 'a';
pre[i] = last[x];
ans += ll(i - pre[i]) * (n - i);
last[x] = i;
}
// for (int i = 0; i < n;i++) //测试pre[i]是否正确
// cout << pre[i] << " " << n-i << endl;
cout << ans;
return 0;
}
题目
【问题描述】
平面上有N 条直线,其中第i条直线是y = Aix+Bi
请计算这些直线将平面分成了几个部分。
【输入格式】
输入的第一行包含一个整数N,
以下N行,包含两个证书 Ai,Bi
【输出格式】
一个整数代表答案
【样例输入】
31
1
2 2
3 3
【样例输出】
6
分析
直接放弃了,试着骗了下分
题目
【问题描述】
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,
则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串lan 排序,只需要1 次交换。对于字符串qiao 排序,
总共需要4 次交换。
小蓝的幸运数字是V,他想找到一个只包含小写英文字母的字符串,对这
个串中的字符进行冒泡排序,正好需要V 次交换。请帮助小蓝找一个这样的字
符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,
请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
【输入格式】
输入一行包含一个整数V,为小蓝的幸运数字。
以下N行,包含两个证书 Ai,Bi
【输出格式】
输出一个字符串,为所求的答案。
【样例输入】
4
【样例输出】
bbaa
分析
当时想了很久,还是太菜了没做出来,用冒泡排序验证了一下,输出了n等于1~8的答案,骗了一下分
大三了第一次参加蓝桥杯(B组省赛),做完下来感受就是,还是以基础为重,注意细节,简单题不要做错!学一些stl库会非常方便。自己还是太菜了,只做对7道题左右,混了个省一,接下来国赛也没怎么准备,估计会白给。