2013年蓝桥杯省赛全面解析系列&&题目思维拓展
一.2013年 C++ B组真题
A.高斯日记
1.题目描述
大数学家高斯有个好习惯:无论如何都要记日记。
他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210
后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?
高斯出生于:1777年4月30日。
在高斯发现的一个重要定理的日记上标注着:5343,因此可算出那天是:1791年12月15日。
高斯获得博士学位的那天日记上标着:8113
请你算出高斯获得博士学位的年月日。
提交答案的格式是:yyyy-mm-dd, 例如:1980-03-21
请严格按照格式,通过浏览器提交答案。
注意:只提交这个日期,不要写其它附加内容,比如:说明性的文字。
2.提取信息
已知某一日期,问过多少天之后的日期是多少?
3.思路与分析
这道题直接可以模拟翻日历的过程,分别需要考虑每个月有不同的天数,以及平年闰年中二月份天数的不同。
不妨想像一下:
1.一天一天过去,每当到了月末一天就要进入下一个月。
2.一月一月过去,每当到了12月31日之后就要进入下一年。
3.如此循环往复,直到到达了指定的天数结束循环。
4.源代码
#include<iostream>
using namespace std;
bool LeapYear(int y){
return (y%4==0&&y%100!=0)||(y%400==0);
}
int main(){
int y=1777;
int m=4;
int d=30;
for(int i=1;i<8113;++i){
d++;
if(m==12&&d==32){
y++;
m=1;
d=1;
continue;
}
if((m==1||m==3||m==5||m==7||m==8||m==10)&&d==32){
m++;
d=1;
continue;
}
if((m==4||m==6||m==9||m==11)&&d==31){
m++;
d=1;
continue;
}
if(m==2&&LeapYear(y)&&d==30){
m++;
d=1;
continue;
}
if(m==2&&!LeapYear(y)&&d==29){
m++;
d=1;
continue;
}
}
cout<<y<<"-"<<m<<"-"<<d;
return 0;
}
5.小结与拓展
本题主要考查对日期类的操作,当然用数组存日期也可以。对于日期类操作可以有许多种不同的题目,现就本小题进行拓展:
1.问几月几号是星期几?
有一个快速办法:假设星期为w,年份为y,月份为m,日期为d
则满足:
w = ( d + 2 m + 3 ( m + 1 ) / 5 + y + y / 4 − y / 100 + y / 400 ) % 7 w=(d+2m+3(m+1)/5+y+y/4-y/100+y/400)\%7 w=(d+2m+3(m+1)/5+y+y/4−y/100+y/400)%7
之后再加一就是星期几了。
注意:1,2月要当成13,14月计算。
B.马虎的算式
1.题目描述
小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。
有一次,老师出的题目是:36 x 495 = ?
他却给抄成了:396 x 45 = ?
但结果却很戏剧性,他的答案竟然是对的!!
因为 36 * 495 = 396 * 45 = 17820
类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54
假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)
能满足形如: ab * cde = adb * ce 这样的算式一共有多少种呢?
请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。
满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。
答案直接通过浏览器提交。
注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。
2.提取信息
满足 a b + c d e = a d b + c e ab+cde=adb+ce ab+cde=adb+ce这样的算式一共有多少个?
3.思路与分析
用五重循环分别枚举a,b,c,d,e五个变量。
4.源代码
#include<iostream>
using namespace std;
int main(){
int ans=0;
for(int a=1;a<=9;++a){
for(int b=1;b<=9;++b){
if(a==b) continue;
else{
for(int c=1;c<=9;++c){
if((a==c)||(b==c)) continue;
else{
for(int d=1;d<=9;++d){
if((a==d)||(b==d)||(c==d)) continue;
else{
for(int e=1;e<=9;++e){
if(a==e||b==e||c==e||d==e) continue;
else{
if((a*10+b)*(c*100+d*10+e)==(a*100+d*10+b)*(c*10+e)){
ans++;
}
}
}
}
}
}
}
}
}
}
cout<<ans;
return 0;
}
5.小结与拓展
本题暴力枚举就行了,没什么可拓展的。A组排他平方数也考了同样的枚举方法。
C.第39级台阶
1.题目描述
小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!
站在台阶前,他突然又想着一个问题:
如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢?
请你利用计算机的优势,帮助小明寻找答案。
要求提交的是一个整数。
注意:不要提交解答过程,或其它的辅助说明文字。
2.提取信息
一次上一个台阶或上两个台阶,且要求左脚起步右脚落地,问上到第39个台阶时有几种方法?
3.思路与分析
第一种做法:此题是典型的动态规划,也可以理解成fib数组的应用,因此使用递推法列写状态转移方程,其中开dp二维数组表示走到第i级台阶时走奇数或者偶数步数有几种走法。
d p [ 0 ] [ i ] = d p [ 1 ] [ i − 1 ] + d p [ 1 ] [ i − 2 ] dp[0][i]=dp[1][i-1]+dp[1][i-2] dp[0][i]=dp[1][i−1]+dp[1][i−2]
d p [ 1 ] [ i ] = d p [ 0 ] [ i − 1 ] + d p [ 0 ] [ i − 2 ] dp[1][i]=dp[0][i-1]+dp[0][i-2] dp[1][i]=dp[0][i−1]+dp[0][i−2]
上式表明在奇数或者偶数步数下走第i级台阶可以由第i-1级台阶和第i-2级台阶分别转移而来。
第二种做法:此题又可以用递归实现,利用dfs搜索状态,此状态可以设置两个参数:第一个参数是剩下的阶梯数,第二个参数是已走的步数。因此每递归一次已走的步数加1,同时往两个方向深搜:走一步和走两步。可以定义函数头:
void dfs(int stairs,int step)
4.源代码
1.动态规划(递推)
#include<iostream>
using namespace std;
int main(){
int dp[2][105];
dp[0][0]=1;
dp[1][0]=0;
dp[0][1]=1;
dp[1][1]=1;
for(int i=2;i<39;++i){
dp[0][i]=dp[1][i-1]+dp[1][i-2];
dp[1][i]=dp[0][i-1]+dp[0][i-2];
}
cout<<dp[1][38];
return 0;
}
2.深搜(递归)
#include<iostream>
using namespace std;
int ans=0;
void dfs(int stairs,int step){
if(stairs<0){
return;
}
if(stairs==0&&step%2==0){
ans++;
return;
}
dfs(stairs-1,step+1);
dfs(stairs-2,step+1);
}
int main(){
dfs(39,0);
cout<<ans;
return 0;
}
5.小结与拓展
走楼梯这道题既可以用dp又可以dfs深搜。
可以说fib数组的热度非常高。现就本小题进行拓展:
每次可以走任意奇数个台阶,问n阶台阶有几种走法?
继续分析状态转移方程:
d p [ i ] = d p [ i − 1 ] + d p [ i − 3 ] + . . . + d p [ 1 ] dp[i]=dp[i-1]+dp[i-3]+...+dp[1] dp[i]=dp[i−1]+dp[i−3]+...+dp[1]
即这级台阶一定是由前面迈奇数个台阶转移而来。
核心代码如下:
for(int i=1;i<=n;++i){
dp[i]=0;
for(int j=i-1;j>=0;j-=2){
//步数为2
dp[i]+=dp[j];
}
}
D.黄金连分数(这题还得想想)
1.题目描述
黄金分割数0.61803… 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。
对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!!
言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。
比较简单的一种是用连分数:
这个连分数计算的“层数”越多,它的值越接近黄金分割数。
请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。
小数点后3位的值为:0.618
小数点后4位的值为:0.6180
小数点后5位的值为:0.61803
小数点后7位的值为:0.6180340
(注意尾部的0,不能忽略)
你的任务是:写出精确到小数点后100位精度的黄金分割值。
注意:尾数的四舍五入! 尾数是0也要保留!
显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。
2.提取信息
输出黄金连分数精确100位的结果。
3.思路与分析
此题暂无思路(涉及到大数运算后续讨论)。
E.前缀判断
1.题目描述
如下的代码判断 needle_start指向的串是否为haystack_start指向的串的前缀,如不是,则返回NULL。
比如:“abcd1234” 就包含了 “abc” 为前缀
char* prefix(char* haystack_start, char* needle_start)