[第四届蓝桥杯省赛C++B组]省赛全题目题解

文章目录

  • 快速分支通道
  • 1.高斯日记
    • 来源: 第四届蓝桥杯省赛C++A/B组
    • 算法标签 模拟
    • 题目描述
    • 输出格式
    • 思路
    • CPP代码
  • 2.马虎的算式
        • 题目描述:
        • 算法标签: 枚举
        • 题目答案:
        • 题目思路:
        • 题目代码:
  • 3.第39级阶梯
    • 来源 第四届蓝桥杯C/C++B组
    • 算法标签:dfs,斐波拉契,动态规划
    • 题目描述
    • 思路
    • cpp代码
  • 4.黄金连分数
    • 来源:第四届蓝桥杯省赛C++B组
    • 算法标签:斐波拉契,高精度
    • 题目描述:
    • 思路:
    • 答案:
    • 题目代码
  • 5.前缀判断
    • 来源:第四届蓝桥杯C/C++B组
    • 算法标签:指针
    • 题目描述:
    • 思路
    • 题目答案:
  • 6.三部排序
        • 来源:2013蓝桥杯 CC++程序设计本科B组
        • 属别:填空题
        • 题目描述:
        • 如果给定数组:
        • 则排序后为:
        • 题目答案:
        • 题目逻辑:
  • 7.错误票据
        • 来源: 第四届蓝桥杯省赛C++A/B组
        • 算法标签:排序,模拟
        • 题目描述
        • 输入格式
        • 输出格式
        • 数据范围
        • 输出样例:
        • C++ 代码
        • 思路
          • 字符串;
          • 数字读入,桶排序想法;
  • 8.翻硬币
        • 来源: 第四届蓝桥杯省赛C++B组
        • 算法标签 递推
        • 输入格式
        • 输出格式
        • 数据范围
        • 输入样例1:
        • 输出样例1:
        • 输入样例2:
        • 输出样例2:
        • 思路
        • C++ 代码
  • 9.带分数
        • 来源: 第四届蓝桥杯省赛C++B组
        • 算法标签 递归搜索剪枝
      • 题目描述
        • 输入格式
        • 输出格式
        • 数据范围
        • 输入样例1:
        • 输出样例1:
        • 输入样例2:
        • 输出样例2:
        • 思路
        • C++ 代码
  • 10.区间连号数
        • 来源: 第四届蓝桥杯省赛C++B组
        • 算法标签 枚举
        • 题目描述
        • 输入格式
        • 输出格式
        • 数据范围
        • 输入样例1:
        • 输出样例1:
        • 输入样例2:
        • 输出样例2:
        • 样例解释
        • 思路
        • 输入样例1:
        • C++ 代码

高斯日记
马虎的算式
第39级阶梯
黄金连分数

前缀判断
三部排序
错误票据
翻硬币
带分数
区间连号数

快速分支通道

1.高斯日记

来源: 第四届蓝桥杯省赛C++A/B组

算法标签 模拟

题目描述

大数学家高斯有个好习惯:无论如何都要记日记。


他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210


后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?


高斯出生于:1777年4月30日。

在高斯发现的一个重要定理的日记上标注着:5343,因此可算出那天是:1791年12月15日。


高斯获得博士学位的那天日记上标着:8113   


请你算出高斯获得博士学位的年月日。

输出格式

提交答案的格式是:yyyy-mm-dd, 例如:1980-03-21

请严格按照格式,通过浏览器提交答案。 注意:只提交这个日期,不要写其它附加内容,比如:说明性的文字。 1799 7 16

思路

本体暴力数日期,然后做日期变更,属实体力活。
我们需要判定的点有“大月”、“小月”、“闰年”。
循环模拟过程即可,还需要注意年份的增加。

CPP代码

#include

using namespace std;
bool leap(int y)//闰年判断
{
     
    return (y%400==0||y%100!=0&&y%4==0);
}
int main()
{
     
    int y=1777,m=4,d=30;
    for(int i=0;i<8113-1;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&&!leap(y)&&d==29)//非闰年
                {
     
                    m++;
                    d=1;
                    continue;
                }
            if(m==2&&leap(y)&&d==30)//闰年
                {
     
                    m++;
                    d=1;
                    continue;
                }
        }
    cout<<y<<" "<<m<<" "<<d;
    return 0;
}

2.马虎的算式

题目描述:

算法标签: 枚举

小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。
有一次,老师出的题目是: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 这样的算式一共有多少种呢?

请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。
满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。

答案直接通过浏览器提交。
注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。

题目答案:

142

题目思路:

我们只需要满足条件: ab * cde = adb * ce
因为数据范围小,所以直接暴力枚举

题目代码:

#include
int main() {
     
    int num = 0;
    for (int a = 1; a < 10; ++a) {
     
        for (int b = 1; b < 10; ++b) {
     
            if (b != a)
                for (int c = 1; c < 10; ++c) {
     
                    if (c != b && c != a)
                        for (int d = 1; d < 10; ++d) {
     
                            if (d != c && d != b && d != a)
                                for (int e = 1; e < 10; ++e) {
     
                                    if (e != d && e != c && e != b && e != a)
                                        if ((a * 10 + b)*(c * 100 + d * 10 + e) == (a * 100 + d * 10 + b)*(c * 10 + e))
                                            num++;
                                }
                        }
                }
        }
    }
    std::cout << num;
}

3.第39级阶梯

来源 第四届蓝桥杯C/C++B组

算法标签:dfs,斐波拉契,动态规划

题目描述

小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!站在台阶前,他突然又想着一个问题:

如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢? 请你利用计算机的优势,帮助小明寻找答案。

要求提交的是一个整数。

思路

1.我们可以从39步走完了去考虑,则最后一步确定,思考前一步(f[n-1]||f[n-2]),这样想可以想想为动态规划问题。
2.直接利用数据优势,暴搜拿出答案。

cpp代码

dfs

#include

using namespace std;
int ans;
void dfs(int step,int n)//step剩下得台阶数,n已经走的步数
{
     
    if(step<0)return;
    if(step==0&&n%2==0)ans++;//满足偶数条件且走完了则+1
    dfs(step-2,n-1);//一次走一个还是两个,全部列出
    dfs(step-1,n-1);
}
int main()
{
     
    dfs(39,0);//从剩39级台阶往下走
    cout<<ans;
    return 0;
}

4.黄金连分数

来源:第四届蓝桥杯省赛C++B组

算法标签:斐波拉契,高精度

题目描述:

标题:黄金连分数
黄金分割数0.61803… 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。
对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!!
言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。
比较简单的一种是用连分数:

                                 1
    黄金数 			= ---------------------
                                     1
                   		1 + -----------------
                                        1
                         		1 + -------------
                                           1
                               		1 + ---------
                                        	1 + ...
                                        

这个连分数计算的“层数”越多,它的值越接近黄金分割数。
请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。
小数点后3位的值为:0.618
小数点后4位的值为:0.6180
小数点后5位的值为:0.61803
小数点后7位的值为:0.6180340
(注意尾部的0,不能忽略)
你的任务是:写出精确到小数点后100位精度的黄金分割值。
注意:尾数的四舍五入! 尾数是0也要保留!
显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。

思路:

黄金连分数其实就是黄金比例,当fib[]有极限时,fib[n-1]/fib[n]就是黄金比,约等于 (根号5-1)/2
我们可以按照上面的那个公式先写几项:1/2,2/3,3/5,5/8,8/13……可以看出其实就是相邻两项斐波那契数相除。

可以用斐波纳契数列和模拟手算除法实现

为什么取到48 和 49 我本来以为是爆int 发现49就爆了,所以我真不知道

答案:

0.6180339887498948481971959525508621220510663574518538453723187601229582821971784348083863296133320592

题目代码

#include
using namespace std;

typedef long long LL;

void div(LL a,LL b,int end,int begin)//模拟手工除法
{
     
    if(begin>end)return ;
    int tmpans=a/b;
    cout<<tmpans;
    div((a%b)*10,b,end,begin+1);
}

int main()
{
     
	unsigned long long f[500]={
     0,1};
	
	for (int i = 2; i<100; i++)f[i] = f[i - 1] + f[i - 2];

    div(f[48],f[49],100,0);

	return 0;
	

}

5.前缀判断

来源:第四届蓝桥杯C/C++B组

算法标签:指针

题目描述:

如下的代码判断 needle_start指向的串是否为haystack_start指向的串的前缀,如不是,则返回NULL。
比如:“abcd1234” 就包含了 “abc” 为前缀

char* prefix(char* haystack_start, char* needle_start)
{
     
	char* haystack = haystack_start;
	char* needle = needle_start;
 
	
	while(*haystack && *needle){
     
		if(______________________________) return NULL;  //填空位置
	}
	
	if(*needle) return NULL;
	
	return haystack_start;
}

请分析代码逻辑,并推测划线处的代码,通过网页提交。
注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!

思路

while(*haystack && *needle){ 的指针用法是表明这两个部分都没有越出字符串的边界。
if(*needle) return NULL;这里是判断如果不是前缀,因为注意比较之后neddle仍有剩余,所以不是 ,返回NULL。
基于以上的原则我们发现代码中明显缺少比对部分,且如果两个字符串不相等依然可以判定不是前缀。
则可以表明有两个推断1.haystack++与needle++,以及2.haystack!= needle
结合来看即为(haystack++) != *(needle++)

题目答案:

*(haystack++) != *(needle++)

6.三部排序

来源:2013蓝桥杯 CC++程序设计本科B组

属别:填空题

题目描述:

一般的排序有许多经典算法,如快速排序、希尔排序等。
但实际应用时,经常会或多或少有一些特殊的要求。我们没必要套用那些经典算法,可以根据实际情况建立更好的解法。

比如,对一个整型数组中的数字进行分类排序:

使得负数都靠左端,正数都靠右端,0在中部。注意问题的特点是:负数区域和正数区域内并不要求有序。可以利用这个特点通过1次线性扫描就结束战斗!!

以下的程序实现了该目标。

其中x指向待排序的整型数组,len是数组的长度。

void sort3p(int* x, int len)
{
     
    int p = 0;
    int left = 0;
    int right = len-1;

    while(p<=right){
     
        if(x[p]<0){
     
            int t = x[left];
            x[left] = x[p];
            x[p] = t;
            left++;
            p++;
        }
        else if(x[p]>0){
     
            int t = x[right];
            x[right] = x[p];
            x[p] = t;
            right--;            
        }
        else{
     
            __________________________;  //填空位置
        }
    }

}

C++

如果给定数组:

25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0

则排序后为:

-3,-2,-16,-5,0,0,0,21,19,33,25,16,18,25

请分析代码逻辑,并推测划线处的代码,通过网页提交

注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!

题目答案:

p++;

题目逻辑:

负数都靠左端,正数都靠右端,0在中部。

  1. p用来记数,left 0,right maxN;
  2. t作swap;
    一这个逻辑来说
    小于零 放左边,大于零 放右边 向中间靠拢,
    为0时 p++,相当于总数-1,即中间最终会存放0!

7.错误票据

来源: 第四届蓝桥杯省赛C++A/B组

算法标签:排序,模拟

题目描述

某涉密单位下发了某种票据,并要在年终全部收回。

每张票据有唯一的ID号。

全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。

因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。

你的任务是通过编程,找出断号的ID和重号的ID。

假设断号不可能发生在最大和最小号。

输入格式

第一行包含整数 N,表示后面共有 N 行数据。

接下来 N 行,每行包含空格分开的若干个(不大于100个)正整数(不大于100000),每个整数代表一个ID号。

输出格式

要求程序输出1行,含两个整数 m,n,用空格分隔。

其中,m表示断号ID,n表示重号ID。

数据范围

1≤N≤100

输入样例:

2
5  6  8  11  9
10  12  9

输出样例:

7 9

C++ 代码

思路

看题目本身只是在数组当中找出重号和缺号,我们只需要排序一下就行了。
难点在于字符串的读入处理上面。

字符串;
#include
#include
#include
#include

using namespace std;
const int N=1e5+10;
int a[N];
int n;

int main()
{
     
    int cnt;
    cin>>cnt;
    
    string line;
    getline(cin,line);
    while(cnt--)// 忽略掉第一行的回车
        {
     
           getline(cin,line);//读入一行
           stringstream ssin(line);//给入ssin
           while(ssin>>a[n])n++;//放入数组
        }
    sort(a,a+n);
    int an1,an2;
    for(int i=1;i<=n;i++)
        {
     
            if(a[i]-a[i-1]>=2)an1=a[i]-1;// 断号
            else if(a[i]-a[i-1]==0)an2=a[i];// 重号
        }
    cout<<an1<<" "<<an2;
    return 0;
}
数字读入,桶排序想法;

利用桶排序的思想,记录所有数组出现的次数。

#include

using namespace std;
const int N=1e5+10;
int a[N];

int main()
{
     
    int cnt;
    cin>>cnt;
    
    int t;
    int maxv=-N,minv=N;
    while(cnt--)
        {
     
            while(cin>>t)
               {
     
                    a[t]++;
                    maxv=max(maxv,t);
                    minv=min(minv,t);
                }
        }
    int an1,an2;
    for(int i=minv;i<=maxv;i++)
            if(!a[i])an1=i;
            else if(a[i]==2)an2=i;
    cout<<an1<<" "<<an2;
    return 0;
}

8.翻硬币

来源: 第四届蓝桥杯省赛C++B组

算法标签 递推

小明正在玩一个“翻硬币”的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:oo*oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作。

输入格式

两行等长的字符串,分别表示初始状态和要达到的目标状态。

输出格式

一个整数,表示最小操作步数

数据范围

输入字符串的长度均不超过100。
数据保证答案一定有解。

输入样例1:

**********
o****o****

输出样例1:

5

输入样例2:

*o**o***o***
*o***o**o***

输出样例2:

1


思路

当前第i个硬币的状态被i-1所决定。
当我们有任意两组硬币时,只能选择同时翻动i,i+1,于是我们连续翻动,知道数组a完全等于数组b则输出次数即可。

C++ 代码

#include
#include

using namespace std;
string a,b;//读取字符串a,b,且其实字符串为a
int cnt;//计数

void turn(int u)//反转硬币
{
     
    if(a[u]=='*')a[u]='o';
    else a[u]='*';
}
int main()
{
     
    cin>>a>>b;
    
    for(int i=0;i<=a.size()-1;i++)if(a[i]!=b[i])turn(i),turn(i+1),cnt++;//因为a,b长度相等所以不管以哪一个为长度终点都没有问题,且如果a[i]与b[i]不相等,则反转两颗硬币,并次数加一。
    cout<<cnt;
    return 0;
}

9.带分数

来源: 第四届蓝桥杯省赛C++B组

算法标签 递归搜索剪枝

题目描述

100 可以表示为带分数的形式:100=3+69258714
还可以表示为:100=82+3546197
注意特征:带分数中,数字 1∼9 分别出现且只出现一次(不包含 0)。

类似这样的带分数,100 有 11 种表示法。

输入格式

一个正整数。

输出格式

输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围

1≤N<106

输入样例1:

100

输出样例1:

11

输入样例2:

105

输出样例2:

6

思路

有题可知带分数中,数字 1∼9 分别出现且只出现一次
且带分数的特性为n=a+b/c,换言之即为n*c=a*c+b;

因为最耿直的做法我们可以将1-10直接暴搜且限制每个数字只出现一次,然后判断是否符合n*c=a*c+b;即可。

C++ 代码

#include

using namespace std;

const int N=1e6+10;
bool st[N];//判断当前数是否被使用
int n,num[N],cnt;

int sum(int l,int r)//取出num中存储的数值,计算a,b,c的数值
{
     
    int t=0;
    for(int i=l;i<=r;i++)
        t=t*10+num[i];
    return t;
}
void dfs(int u)
{
     
    if(u>9)//如果1到9放置完毕
        {
     
            for(int i=1;i<=9;i++)
                for(int j=i+1;j<=9;j++)//用二维遍历所有的长度可能性
                    {
     
                        int a=sum(1,i);
                        int b=sum(i+1,j);
                        int c=sum(j+1,9);//a,b,c的长度被分成三段限制
                        if(n*c==a*c+b)cnt++;//如果满足判断条件则加1
                    }
            return ;
        }
    for(int i=1;i<=9;i++)//暴搜1-9的排列
        {
     
            if(!st[i])//如果当前数字没有被选择
                {
     
                    st[i]=true;//选择,下一次不能再被使用
                    num[u]=i;//放入数组num
                    dfs(u+1);//判断下一个数字
                    num[u]=0;
                    st[i]=false;
                }
        }
}
int main()
{
     
    cin>>n;
    dfs(1);//从1开始跑,绝对够直白
    cout<<cnt;//输出有多少种满足条件的带分数
    return 0;
}

10.区间连号数

来源: 第四届蓝桥杯省赛C++B组

算法标签 枚举

题目描述

小明这些天一直在思考这样一个奇怪而有趣的问题:

在 1∼N 的某个排列中有多少个连号区间呢?

这里所说的连号区间的定义是:

如果区间 [L,R] 里的所有元素(即此排列的第 L 个到第 R 个元素)递增排序后能得到一个长度为 R−L+1 的“连续”数列,则称这个区间连号区间。

当 N 很小的时候,小明可以很快地算出答案,但是当 N 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。

输入格式

第一行是一个正整数 N,表示排列的规模。

第二行是 N 个不同的数字 Pi,表示这 N 个数字的某一排列。

输出格式

输出一个整数,表示不同连号区间的数目。

数据范围

1≤N≤10000,
1≤Pi≤N

输入样例1:

4
3 2 4 1

输出样例1:

7

输入样例2:

5
3 4 2 5 1

输出样例2:

9

样例解释

第一个用例中,有 7 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]
第二个用例中,有 9 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[1,5],[2,2],[3,3],[4,4],[5,5]

思路

如果区间 [L,R] 里的所有元素(即此排列的第 L 个到第 R 个元素)递增排序后能得到一个长度为 R−L+1 的“连续”数列,则称这个区间连号区间。

输入样例1:

4
3 2 4 1

第一个用例中,有 7 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]

1.因为连号必须满足 A.单调递增子序列 B.自身
2.因为 任意子序列中 虽有的数字必须被使用完 才能表明符合单调递增子序列
3.模拟成 左右两个端点循环 当右端点-左端点 等于 当前最大值 - 当前最小值
则表明 从左端点到右端点的所有数都被使用 是单调递增子序列 (且 该情况对a[i] - a[i] 同样适用
则答案+1

枚举所有区间,如果最大值减最小值等于区间长度,则表明用光了所有数据且是连号,则是连号区间

C++ 代码

#include

using namespace std;
const int N=1e4+10,INF=1e8;
int a[N];

int main()
{
     
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    
    int cnt=0;
    for(int i=1;i<=n;i++)   
        {
     
            int maxv=-INF,minv=INF;
            for(int j=i;j<=n;j++)
                {
     
                    maxv=max(a[j],maxv),minv=min(a[j],minv);
                    if(maxv-minv==j-i)cnt++;
                }
        }
    cout<<cnt;
    return 0;
}

你可能感兴趣的:(蓝桥杯,第四届蓝桥杯,2013届蓝桥杯)