2014年第五届蓝桥杯省赛 C/C++真题混搭题解

1.标题:武功秘籍


    小明到X山洞探险,捡到一本有破损的武功秘籍(2000多页!当然是伪造的)。他注意到:书的第10页和第11页在同一张纸上,但第11页和第12页不在同一张纸上。
    小明只想练习该书的第81页到第92页的武功,又不想带着整本书。请问他至少要撕下多少张纸带走?
这是个整数,请通过浏览器提交该数字,不要填写任何多余的内容。

 

答案:7
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	int cnt;
	cnt=0;
	for(int i=10; i<=92; i+=2) {
		cnt++;
		cout<<i<<' '<<i+1<<' '<<cnt<<endl;
	}
	return 0;
}

 

2.标题:等额本金

    小明从银行贷款3万元。约定分24个月,以等额本金方式还款。
    这种还款方式就是把贷款额度等分到24个月。每个月除了要还固定的本金外,还要还贷款余额在一个月中产生的利息。
    假设月利率是:0.005,即:千分之五。那么,
    第一个月,小明要还本金 1250, 还要还利息:30000 * 0.005,总计 1400.00
    第二个月,本金仍然要还 1250, 但利息为:(30000-1250) * 0.005 总计 1393.75
    请问:小明在第15个月,应该还款多少(本金和利息的总和)?

    请把答案金额四舍五入后,保留两位小数。注意:32.5,一定要写为:32.50
    通过浏览器提交答案,这是一个含有小数点和两位小数的浮点数字。不要写多余内容(例如:多写了“元”或添加说明文字)
 

答案:1312.50
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	double li,ben=1250;
	for(int i=0;i<24;i++){
		li=0.005*(30000-ben*i);
		printf("%d %.2f\n",i+1,li+ben);
	}
	return 0;
}

 

3.标题:猜年龄

    小明带两个妹妹参加元宵灯会。别人问她们多大了,她们调皮地说:“我们俩的年龄之积是年龄之和的6倍”。小明又补充说:“她们可不是双胞胎,年龄差肯定也不超过8岁啊。”
    请你写出:小明的较小的妹妹的年龄。

注意: 只写一个人的年龄数字,请通过浏览器提交答案。不要书写任何多余的内容。

 

答案:10
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	for(int i=1;i<=100;i++){
		for(int j=1;j<=100;j++){
			if(i!=j&&abs(i-j)<=8&&i*j==6*(i+j)) cout<<i<<' '<<j<<endl;
		}
	}
	return 0;
}

 

4.标题:大衍数列


    中国古代文献中,曾记载过“大衍数列”, 主要用于解释中国传统文化中的太极衍生原理。
    它的前几项是:0、2、4、8、12、18、24、32、40、50 ...
    其规律是:对偶数项,是序号平方再除2,奇数项,是序号平方减1再除2。
    以下的代码打印出了大衍数列的前 100 项。

int main()
{
    int i;
    for(i=1; i<100; i++){
        if(i%2==0) //填空
            printf("%d ", i*i/2);
        else
            printf("%d ", (i*i-1)/2);
    }
    printf("\n");
}

    请填写划线部分缺失的代码。通过浏览器提交答案。
注意:不要填写题面已有的内容,也不要填写任何说明、解释文字。

 

5.标题:锦标赛


   如果要在n个数据中挑选出第一大和第二大的数据(要求输出数据所在位置和值),使用什么方法比较的次数最少?我们可以从体育锦标赛中受到启发。
   如图【1.png】所示,8个选手的锦标赛,先两两捉对比拼,淘汰一半。优胜者再两两比拼...直到决出第一名。
   第一名输出后,只要对黄色标示的位置重新比赛即可。
   下面的代码实现了这个算法(假设数据中没有相同值)。
   代码中需要用一个数组来表示图中的树(注意,这是个满二叉树,不足需要补齐)。它不是存储数据本身,而是存储了数据的下标。   
   第一个数据输出后,它所在的位置被标识为-1

 

//本题思路类似于间接寻址的堆排序,输出第一大和第二大的数,即原数据不动,用各个数据的 index 进行堆排序,对于元素数据类型较大的情况下,用 index 进行间接寻址排序的话,可以减少很多数据的搬移,效率相对的要高。这里是用 b[]记录元素的index , a[]记录原始数据。

//重新决出k号位置,v为已输出值 
void pk(int* a, int* b, int n, int k, int v)
{
    int k1 = k*2 + 1;
    int k2 = k1 + 1;
    
    if(k1>=n || k2>=n){
        b[k] = -1;
        return;
    }
    
    if(b[k1]==v) 
        pk(a,b,n,k1,v);
    else
        pk(a,b,n,k2,v);
    
    //重新比较
    if(b[k1]<0){
        if(b[k2]>=0)
            b[k] = b[k2]; 
        else
            b[k] = -1;
        return;
    }
    
    if(b[k2]<0){
        if(b[k1]>=0)
            b[k] = b[k1]; 
        else
            b[k] = -1;
        return;
    }
    
    if(a[b[k1]]>a[b[k2]]) //填空         //比较b[k1]与b[k2]所对应的值谁更大
        b[k] = b[k1];
    else
        b[k] = b[k2];
}

//对a中数据,输出最大,次大元素位置和值 
void f(int* a, int len)
{
    int n = 1;
    while(n<len) n *= 2;            //建立完全二叉树——下标范围
    
    int* b = (int*)malloc(sizeof(int*) * (2*n-1));      //分配存放二叉树的数组空间
    int i;
    for(i=0; i<n; i++){ 
        if(i<len) 
            b[n-1+i] = i;                 
        else
            b[n-1+i] = -1;
    }
    
    //从最后一个向前处理                   //建大顶堆
    for(i=2*n-1-1; i>0; i-=2){           
        if(b[i]<0){
            if(b[i-1]>=0)
                b[(i-1)/2] = b[i-1]; 
            else
                b[(i-1)/2] = -1;
        }
        else{
            if(a[b[i]]>a[b[i-1]])
                b[(i-1)/2] = b[i];
            else
                b[(i-1)/2] = b[i-1];
        }
    }
    
    //输出树根
    printf("%d : %d\n", b[0], a[b[0]]);      //输出分别为最大元素下标、最大元素的值
    
    //值等于根元素的需要重新pk
    pk(a,b,2*n-1,0,b[0]);
    
    //再次输出树根
    printf("%d : %d\n", b[0], a[b[0]]);      //输出分别为次大元素下标、次大元素的值
    
    free(b);
}


int main()
{
    int a[] = {54,55,18,16,122,17,30,9,58};
    f(a,9);    
}

    请仔细分析流程,填写缺失的代码。
    通过浏览器提交答案,只填写缺失的代码,不要填写已有代码或其它说明语句等。

读题很重要!想想你做完题后分析的头头是道的样子,那才是你在考场上应该有的姿态。

 6.标题:猜字母


    把abcd...s共19个字母组成的序列重复拼接106次,得到长度为2014的串。
    接下来删除第1个字母(即开头的字母a),以及第3个,第5个等所有奇数位置的字母。
    得到的新串再进行删除奇数位置字母的动作。如此下去,最后只剩下一个字母,请写出该字母。
答案是一个小写字母,请通过浏览器提交答案。不要填写任何多余的内容。

 

答案:q
转化为留下偶数个字母
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int main (){
	int n=106;
	string str,s="abcdefghijklmnopqrs",tmp;
	while(n--) str+=s;
	while(str.size()!=1){
		tmp="";
		for(int i=1;i<str.size();i+=2)
			tmp+=str[i];      //str.replace(i,1," ");  (错误,因为size实时更新!)
				str=tmp;
	}
	cout<<str;
	return 0;
}

 

7.标题:扑克序列

    A A 2 2 3 3 4 4, 一共4对扑克牌。请你把它们排成一行。
    要求:两个A中间有1张牌,两个2之间有2张牌,两个3之间有3张牌,两个4之间有4张牌。
    请填写出所有符合要求的排列中,字典序最小的那个。

例如:22AA3344 比 A2A23344 字典序小。当然,它们都不是满足要求的答案。
请通过浏览器提交答案。“A”一定不要用小写字母a,也不要用“1”代替。字符间一定不要留空格。

 

//2342A3A4
//'2'、'3'、'4'、'A'的ASC码依次为50,51,52,65
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int num[5];
bool judge(int a[]){
	memset(num,-1,sizeof num);
	for(int i=0;i<8;i++){
		if(num[a[i]]!=-1)     //存放上一个a[i]的位置,可能为0,故初始化为-1               			
                        num[a[i]]=abs(i-num[a[i]]-1);
		else 
			num[a[i]]=i;
	}
	for(int i=1;i<=4;i++)
		if(num[i]!=i) return false;
	return true;
}
int main(){
	int a[8]={2,2,3,3,4,4,1,1};  //在这里用1代替A
	do{
		if(judge(a)){
			for(int i=0;i<8;i++)
				cout<<a[i];
			cout<<endl;
		}
	}while(next_permutation(a,a+8));
}
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int a[9]={0,1,1,2,2,3,3,4,4};
int num[5];
bool judge(){
	memset(num,0,sizeof num);
	for(int i=1;i<=8;i++){
		if(num[a[i]]!=0) num[a[i]]=abs(i-num[a[i]])-1;
		else num[a[i]]=i;
	}
	for(int i=1;i<=4;i++)
		if(num[i]!=i) return false;
	return true;
}
int main(){
	do{
		if(judge()){
			for(int i=1;i<=8;i++)
				cout<<a[i];
				cout<<endl;
		} 
	}while(next_permutation(a+1,a+9));
	return 0;
}

 

 

8.标题:分糖果

    有n个小朋友围坐成一圈。老师给每个小朋友随机发偶数个糖果,然后进行下面的游戏:
    每个小朋友都把自己的糖果分一半给左手边的孩子。
    一轮分糖后,拥有奇数颗糖的孩子由老师补给1个糖果,从而变成偶数。
    反复进行这个游戏,直到所有小朋友的糖果数都相同为止。
    你的任务是预测在已知的初始糖果情形下,老师一共需要补发多少个糖果。


【格式要求】
    程序首先读入一个整数N(2<N<100),表示小朋友的人数。
    接着是一行用空格分开的N个偶数(每个偶数不大于1000,不小于2)
    要求程序输出一个整数,表示老师需要补发的糖果数。


例如:输入
3
2 2 4
程序应该输出:
4

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。


注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。


提交时,注意选择所期望的编译器类型。
 

#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int n;
int a[105];
int judge(){
	int x=a[1];
	for(int i=2;i<=n;i++)
		if(a[i]!=x) return 0;
	return 1;
}
int main () {
	int ans=0,tmp;
	cin>>n;
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	while(judge()==0) {
		tmp=a[1]/2;                                   //防数据遗失
		for(int i=1;i<n;i++){ 
			a[i]=a[i]/2+a[i+1]/2;
			if(a[i]%2){
				a[i]++;
				ans++;
			}
		}
		a[n]=a[n]/2+tmp; 
		if(a[n]%2){
			a[n]++;
			ans++;
		}
	}
	cout<<ans;
	return 0;
}

 


9.标题:斐波那契

    斐波那契数列大家都非常熟悉。它的定义是:

    f(x) = 1                    .... (x=1,2)
    f(x) = f(x-1) + f(x-2)      .... (x>2)

    对于给定的整数 n 和 m,我们希望求出:
    f(1) + f(2) + ... + f(n)  的值。但这个值可能非常大,所以我们把它对 f(m) 取模。
    公式参见【图1.png】 

    但这个数字依然很大,所以需要再对 p 求模。

【数据格式】
输入为一行用空格分开的整数 n m p (0 < n, m, p < 10^18)
输出为1个整数

例如,如果输入:
2 3 5
程序应该输出:
0

再例如,输入:
15 11 29
程序应该输出:
25

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。


 

 

四十分代码,待更新……
#include<iostream>
#include<vector>
#define ll long long
using namespace std;
vector<long long> v;
ll n,m,p,tmp,sum=0;
void init() {
	tmp=max(n,m);
	v.push_back(0);
	v.push_back(1);
	v.push_back(1);
	for(int i=3; i<=tmp; i++)
		v.push_back(v[i-1]+v[i-2]);
}
int main() {
	cin>>n>>m>>p;
	init();
	for(int i=1; i<=n; i++) {
		sum=(sum+v[i])%v[m];
	}
	sum=sum%p;
	cout<<sum<<endl;
	return 0;
}

 

 

10.标题:矩阵翻硬币

    小明先把硬币摆成了一个 n 行 m 列的矩阵。
    随后,小明对每一个硬币分别进行一次 Q 操作。
    对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。
    其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
    当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
    小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
    聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。

【数据格式】
    输入数据包含一行,两个正整数 n m,含义见题目描述。
    输出一个正整数,表示最开始有多少枚硬币是反面朝上的。

【样例输入】
2 3

【样例输出】
1

【数据规模】
对于10%的数据,n、m <= 10^3;
对于20%的数据,n、m <= 10^7;
对于40%的数据,n、m <= 10^15;
对于10%的数据,n、m <= 10^1000(10的1000次方)。

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

 

以下解析摘自网络,后文附有原文链接 

1.很容易得出,如果一枚硬币被翻了奇数次,那么它原来的状态肯定是反面朝上,所以,我们要找的就是被翻了奇数次的硬币
2. 根据Q操作定义,我们举个例子,对于(2,3)这个点只有在(1,1)(1,3)(2,1)(2,3)这四个点进行Q操作时才翻转,一共翻转了4次,通过多个例子总结不难看出,(x,y)点再所有点进行完Q操作后翻转的次数为a*b次,其中a为x的约数,b为y的约数。因此若想要这个硬币被翻奇数次,a和b必须都得是奇数,即x和y都有奇数个约数。


先普及一个数论知识完全平方数有奇数个约数
那个这个问题就转化成了——输入两个数n,m,设小于等于n的完全平方数的个数是a,小于等于m的完全平方数的个数是b,求a*b。那么怎么求小于等于n和m完全平方数的个数呢?
再普及一个知识:

那么这个题目有转化成了——输入两个数n,m,求[sqrt(n)]*[sqrt(m)]。
这样题目就简单很多了,只要解决两个问题就可以
1.大数的乘法
2.大数的平方根取整


问题1解决方法:
先弄两个代表数值的字符串s1,s2,将s1的每一位与s2相乘,存到一个整形数组里
举个例子s1=“123”,s2=“89”
123*89

规律:将s1[i]*s2[j]存入num[i+j+1]中,因为num[0]要存最后进位存完后,对num数组进行倒着计算,比如27实际上就是123*89计算中3*9=27,即把7留下2进上去

最后结果就是10947


问题2解决方法:

这个开方方法不是我想出来的,是参照了大神的一个方法

假如一个数有偶数位n,那么这个数的方根有n/2位;如果n为奇数,那么方根为(n+1)/2位

然后,让我们实际看一个例子,我们假设这个数就是1200

1.很明显,它有4位,所以它的方根有2位,然后,我们通过下面的方法来枚举出它的整数根

00*00=0<1200

10*10=100<1200

20*20=400<1200

 30*30=900<1200

40*40=1600>1200

所以,这个根的十位就是3,然后,再枚举个位

 31*31=961<1200

32*32=1024<1200

33*33=1089<1200

34*34=1156<1200

所以,这个根就是34,因为平方增长的速度还是比较快的,所以速度没有太大问题。为了提高速度,我们可以优化比较函数:

还拿上面的例子来说

 

30*30=900<1200

40*40=1600>1200
这两个式子,一般来说,我们应该先计算3*3=9,然后在9后面添2个0再与1200比较,但由于数据量很大,添零也会消耗时间

于是我们可以计算需要加的0的数量然后用下面的方法直接比较

1.如果第i个数的平方的位数加上需要添加的零的个数之后位数与原数不相等,那么位数大的数值大

2.如果位数相等就没必要再添零,直接进行字符串比较即可

例如:

 

30*30=900<1200

40*40=1600>1200

十位是3的时候 3*3=9是1位填上两个零后位数位3位小于1200的4位所以900<1200

十位是4的时候 4*4=16是两位,添上两个零后位数为4位等于1200的四位,所以只需比较字符串16与1200的大小

很明显在字符串中16>1200,所以1600>1200

那么添加零的个数怎么算呢?

假设一个数的平方根取整的位数为n,从前往后算目前计算到了第i位,则需要添加2*(n-1-i)个零

例如:1200 平方根取整有2位,目前算到了第0位(从0开始计数)即30*30(我们算的是3*3),需要加2*(2-1-0)=2个零
原文:https://blog.csdn.net/reidsc/article/details/64924051 

#include <iostream>
#include <algorithm>
using namespace std;
string StrMul(string s1,string s2) { //大数乘法
	string ans;
	int num[500]= {0},i,j;
	for(i=0; i<s1.length(); i++) //s计算存到num中
		for(j=0; j<s2.length(); j++)
			num[i+j+1]+=(s1[i]-'0')*(s2[j]-'0');
	for(i=s1.length()+s2.length()-1; i>0; i--) //num的处理
		if(num[i]>=10)		{
			num[i-1]+=num[i]/10;
			num[i]%=10;
		}
	for(int i=0; i<=s1.length()+s2.length()-1; i++) //将num数存到ans字串中,注意进位为0的情况
		if(!i&&num[i]||i)
			ans.push_back(num[i]+'0');
	return ans;
}
bool StrCmp(string s1,string s2,int pos) { //比较两字符串大小,pos代表应该在s1后面填几个零

	if(s1.length()+pos!=s2.length())//如果s1位数不等于s2,
		return s1.length()+pos>s2.length();
	else//位数相等
		return s1>s2;
}
string SqrtStr(string s) { //大数平方根取整
	int len;
	string ans;
	if(s.length()%2==0)//长度为偶数
		len=s.length()/2;
	else
		len=s.length()/2+1;
	for(int i=0; i<len; i++) { //一位一位的循环
		ans.push_back('0');
		for(int j=0; j<=9; j++)		{
			if(StrCmp(StrMul(ans,ans),s,2*(len-1-i)))//需要添加0的个数是2*(len-1-i)解析见上面
				break;
			ans[i]++;
		}
		ans[i]--;
	}
	return ans;
}
int main() {
	string s1,s2;
	cin>>s1>>s2;
	cout<<StrMul(SqrtStr(s1),SqrtStr(s2))<<endl;
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
int num[505];
void BigMul(string s1,string s2){
	int len1=s1.size();
	int len2=s2.size();
	for(int i=0;i<len1;i++)
		for(int j=0;j<len2;j++)
			num[i+j+1]+=(s1[i]-'0')*(s2[j]-'0');
	for(int i=len1+len2-1;i>0;i--){//范围0~len1+len2-1
		if(num[i]>=10){
			num[i-1]+=num[i]/10;
			num[i]%=10;
		}
	}
	string ans;	
	for(int i=0;i<=len1+len2-1;i++)
		if(!i&&num[i]||i) ans.push_back(num[i]+'0');
	cout<<ans;
}
int main(){
	string s1,s2;
	cin>>s1>>s2;
	BigMul(s1,s2);
	return 0;
}

 


 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值