蓝桥杯第七届 省赛 c语言A组

第一题

网友年龄

某君新认识一网友。
当问及年龄时,他的网友说:
“我的年龄是个2位数,我比儿子大27岁,
如果把我的年龄的两位数字交换位置,刚好就是我儿子的年龄”

请你计算:网友的年龄一共有多少种可能情况?

提示:30岁就是其中一种可能哦.

请填写表示可能情况的种数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】

写一个循环即可解决问题。

答案:7

#include <iostream>
using namespace std;

int main() {
	int rec = 0, note;
	for (int i = 1; i <= 99; i++) {
		note = (i%10)*10 + i/10;
		if (i - note == 27)
			rec++;
	}
	cout << rec << endl;
	return 0;
}

 

第二题

生日蜡烛

某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。

现在算起来,他一共吹熄了236根蜡烛。

请问,他从多少岁开始过生日party的?

请填写他开始过生日party的年龄数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】

由于是填空题,用了两个简单的循环。

答案:26

#include <iostream>
using namespace std;

int main() {
	int rec, t;
	for (int i = 1; i <= 100; i++) {
		rec = 0;
		t = i;
		while (rec < 236) {
			rec += t;
			t++;
		}
		if (rec == 236) {
			cout << i << endl;
			break;
		}
	}
	return 0;
}

 

第三题

方格填数

如下的10个格子

填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】

把所有的情况枚举一遍,把不连续的要求全部列出来,同时满足则找到一种方案。

答案:1580

#include <iostream>
#include <string>
#include <cstdlib>
#include <algorithm>
using namespace std;

bool cn(string s) {
	if (abs(s[0]-s[1]) != 1 && abs(s[0]-s[3]) != 1 && abs(s[0]-s[4]) != 1 && abs(s[0]-s[5]) != 1 &&
		abs(s[1]-s[2]) != 1 && abs(s[1]-s[4]) != 1 && abs(s[1]-s[5]) != 1 && abs(s[1]-s[6]) != 1 &&
		abs(s[2]-s[5]) != 1 && abs(s[2]-s[6]) != 1 &&
		abs(s[3]-s[4]) != 1 && abs(s[3]-s[7]) != 1 && abs(s[3]-s[8]) != 1 &&
		abs(s[4]-s[5]) != 1 && abs(s[4]-s[7]) != 1 && abs(s[4]-s[8]) != 1 && abs(s[4]-s[9]) != 1 &&
		abs(s[5]-s[6]) != 1 && abs(s[5]-s[8]) != 1 && abs(s[5]-s[9]) != 1 &&
		abs(s[6]-s[9]) != 1 && abs(s[7]-s[8]) != 1 && abs(s[8]-s[9]) != 1)
		return true;
	else
		return false;
}

int main() {
	string s = "0123456789";
	int rec = 0;
	do {
		if (cn(s)) rec++;
	} while (next_permutation(s.begin(), s.end()));
	cout << rec << endl;
	
	return 0;
}

 

第四题

快速排序

排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。

其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。

这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。

下面的代码是一种实现,请分析并填写划线部分缺少的代码。

#include <stdio.h>

void swap(int a[], int i, int j)
{
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}

int partition(int a[], int p, int r)
{
    int i = p;
    int j = r + 1;
    int x = a[p];
    while(1){
        while(i<r && a[++i]<x);
        while(a[--j]>x);
        if(i>=j) break;
        swap(a,i,j);
    }
    ________________________;
    return j;
}

void quicksort(int a[], int p, int r)
{
    if(p<r){
        int q = partition(a,p,r);
        quicksort(a,p,q-1);
        quicksort(a,q+1,r);
    }
}
    
int main()
{
	int i;
	int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
	int N = 12;
	
	quicksort(a, 0, N-1);
	
	for(i=0; i<N; i++) printf("%d ", a[i]);
	printf("\n");
	
	return 0;
}


注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。

【思路】

了解快排的同学应该清楚,最终的目标是将"标尺"放到适当的位置,由于这个程序选择的"标尺"在排序的左边,所以应该拿"标尺"与小于"标尺"的最右边的元素交换。

答案:swqp(a,p,j)

 

第五题

消除尾一

下面的代码把一个整数的二进制表示的最右边的连续的1全部变成0
如果最后一位是0,则原数字保持不变。

如果采用代码中的测试数据,应该输出:
00000000000000000000000001100111   00000000000000000000000001100000
00000000000000000000000000001100   00000000000000000000000000001100

请仔细阅读程序,填写划线部分缺少的代码。

#include <stdio.h>

void f(int x)
{
	int i;
	for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);
	printf("   ");
	
	x = ___________;
	
	for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);
	printf("\n");	
}

int main()
{
	f(103);
	f(12);
	return 0;
}

注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。

【思路】

一个二进制数,加1,末位连续的1全为0,进位加1,再与原来的数进行与操作,得到结果。

答案:x&(x+1)

 

第六题

寒假作业

现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:

   □ + □ = □
   □ - □ = □
   □ × □ = □
   □ ÷ □ = □


每个方块代表1~13中的某一个数字,但不能重复。
比如:
 6  + 7 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

以及: 
 7  + 6 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

就算两种解法。(加法,乘法交换律后算不同的方案)
 
你一共找到了多少种方案?


请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】

深搜+剪枝,别人的思路,剪枝剪得很妙,赞一个!(又学到东西了,开心。)

答案:64

#include <iostream>
#include <cstring>
using namespace std;

int visit[13], a[12];
int rec = 0;

void dfs(int t) {
	if (t >= 3 && a[0] + a[1] != a[2])  //剪枝
		return ;
	if (t >= 6 && a[3] - a[4] != a[5])
		return ;
	if (t >= 9 && a[6] * a[7] != a[8])
		return ;
	if (t >= 12 && a[9] == a[10] * a[11]) {
		rec++;
		return ;
	}
	if (t >= 12)
		return;
	for (int i = 1; i <= 13; i++) {
		if (!visit[i]) {
			visit[i] = 1;
			a[t] = i;
			dfs(t+1);
			visit[i] = 0;
		}
	}
}

int main() {
	memset(a, 0, sizeof(a));
	memset(visit, 0, sizeof(visit));
	dfs(0);
	cout << rec << endl;
	
	return 0;
}

 

第七题

剪邮票

有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,

中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】

参考别人的思路。

1.遍历所有的方案

2.每个方案利用深搜判断是否链接,链接则方案数加一

重点在于深搜的设计。

首先存储到某个方案,调用深搜,dfs()函数遍历到某个点,判断它周围的四个点是否在方案里面,如果在方案里面,且该点未曾被遍历到,则标记该点被遍历。

可以利用每个点的数值得到周围的四个点,但由于4和5不相连,8和9不相连,改变方格的值为:

1     2    3    4

6     7    8    9

11  12  13  14

这样,每个点遍历上下左右的点的距离分别为{-5,+5,-1,+1}

纤细设计兄弟们去体会下代码吧。。。我尽力了。

#include <iostream>
#include <cstring>
using namespace std;

const int num = 5;
int a[num], visit[num], t;
int map[] = {1,2,3,4,6,7,8,9,11,12,13,14};
int b[] = {-1, 1, -5, 5};

void dfs(int n) {
    for (int i = 0; i < 4; i++) { //尝试当前点的四个方向
        t = a[n] + b[i];
        if (t == 5 || t == 10 || t > 14 || t < 1) //t的值不在表格内,遍历下一个方向
            continue;
        for (int j = 0; j < 5; j++) 
            if (!visit[j] && t == a[j]) { //当前点的某一个方向的点在当前方案中
                visit[j] = 1;
                dfs(j);
            }
    }
}

int main(){
    bool flag;
    int rec = 0;
    for (int i = 0; i < 12; i++) 
        for (int j = i + 1; j < 12; j++)
            for (int k = j + 1; k < 12; k++)
                for (int l = k + 1; l < 12; l++)
                    for (int n = l + 1; n < 12; n++) {
                        a[0] = map[i];
                        a[1] = map[j];
                        a[2] = map[k];
                        a[3] = map[l];
                        a[4] = map[n];
                        memset(visit, 0, sizeof(visit));
                        dfs(0);
                        flag = true;
                        for (int m = 0; m < num; m++)
                            if (!visit[m]) {
                                flag = false;
                                break;
                            }
                        if (flag == true)
                            rec ++;
                     }
    cout << rec << endl;

    return 0;
}

 

第八题

四平方和

四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。

比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)

对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法


程序输入为一个正整数N (N<5000000)
要求输出4个非负整数,按从小到大排序,中间用空格分开

例如,输入:
5
则程序应该输出:
0 0 1 2

再例如,输入:
12
则程序应该输出:
0 2 2 2

再例如,输入:
773535
则程序应该输出:
1 1 267 838

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

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

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

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

【思路】

暴力破解,枚举4个数,符合条件结束程序,用三个循环即可,第四个数直接计算出来,降低时间复杂度。

#include <iostream>
#include <cmath>
using namespace std;
const int NUM = 5000000;

int main(){
    int num = sqrt(5000000);
    int n, d;
    bool flag = false;
    cin >> n;
    for (int a = 0; a <= num; a++)  
        for (int b = 0; b <= num; b++)  
            for (int c = 0; c <= num; c++) { 
                d = sqrt(n - (a * a + b * b + c * c));
                if (n == a * a + b * b + c * c + d * d) {
                    if (c > d) {
                        int temp = c;
                        c = d;
                        d = temp;
                    }

                    cout << a << ' ' << b << ' ' << c << ' ' << d << endl; 

                    return 0;
                }
            }
}

 

这里呈上别人的思路。用一个数组存储所有小于n的数中能被2个数的平方和求得的数。从这些数中找出符合条件的数即可,具体实现看代码。这种方法有效地减低了时间复杂度,但是需要消耗更多的内存。

#include <iostream>
#include <cmath>
using namespace std;

const int NUM = 5000010;

int map[NUM] = {0};
int n;

void f() {
    for (int i = 0; i * i <= n; i++)
        for (int j = 0; j * j <= n; j++) 
            if (i * i + j * j <= n)
                map[i * i + j * j] = 1; //这个数可以用两个数平方求得
                
}


int main(){
    cin >> n;
    f();
    for (int i = 0; i * i <= n; i++) {
        for (int j = 0; j * j <= n; j++) {
            if (map[n - i * i - j * j] == 0)
                continue;
            for (int k = 0; k * k <= n; k++)  {
                int p = sqrt(n - i * i - j * j - k * k);
                if ((i * i + j * j + k * k + p * p) == n) {
                    cout << i << ' ' << j  << ' ' << k << ' ' << p << endl;
                    return 0;
                }
            }
        }
    }
}

 

第九题

密码脱落

X星球的考古学家发现了一批古代留下来的密码。
这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。

你的任务是:
给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

输入一行,表示现在看到的密码串(长度不大于1000)
要求输出一个正整数,表示至少脱落了多少个种子。

例如,输入:
ABCBA
则程序应该输出:
0

再例如,输入:
ABDCDCBABC
则程序应该输出:
3

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

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

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

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

【思路】

很惭愧还是大神的思路,源博主找不到了,因为看的博客没有标明,学习一下。

用的方法还是我们熟悉的深搜,但是我觉得需要在充分了解题目的基础上,才能知道用这样的判断可以得到答案。。。

#include <iostream>
#include <string>
using namespace std;

int num = 100000;
string str;

void dfs(int l, int r, int len) {
    if (l >= r) 
        num = (num < len ? num : len);
    else {
        if (str[l] == str[r])
            dfs(l + 1, r - 1, len);
        else {
            dfs(l + 1, r, len + 1);
            dfs(l, r - 1, len + 1);
        }
    } 
    return ;
}
    

int main(){

    cin >> str;

    dfs(0, str.size() - 1, 0);

    cout << num << endl;

    return 0;
}

 

 

 

posted @ 2019-03-08 08:14  JonnyOu1012  阅读(26)  评论(0编辑  收藏  举报