2019年第十届蓝桥杯大赛软件类省赛C/C++大学B组题解

试题 A:组队

本题总分:5分
【问题描述】
作为篮球队教练,你需要从以下名单中选出 1号位至 5号位各一名球员,
组成球队的首发阵容。
每位球员担任 1号位至 5号位时的评分如下表所示。请你计算首发阵容 1
号位至5号位的评分之和最大可能是多少?

答案:490(该题需考虑清楚,一个人不能担任两个位置,98+99+98+97+98=490)

 

 

试题 B:年号字串

本题总分:5分
【问题描述】
小明用字母 A对应数字 1,B对应 2,以此类推,用 Z对应 26。对于 27
以上的数字,小明用两位或更长位的字符串来对应,例如 AA对应27,AB对
应28,AZ对应52,LQ对应329。
请问2019对应的字符串是什么?

答案:BYQ

(该题我做的时候是直接算了2019/26=77,AZ是52,所以BY对应77,2019%26=17,Q为17,所以答案为BYQ)

但该题考查的是26进制,代码如下:

 

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
    int x = 2019;
    while(x){
        printf("%c", char(x%26+64));
        x/=26;
    }
    return 0;
}
//结果取反

 

 

试题C:数列求值

本题总分:10分
【问题描述】
给定数列1,1,1,3,5,9,17,…,从第4项开始,每项都是前3项的和。求
第20190324项的最后4位数字

答案:4659

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
    int a, b, c, d;
    a = b = c = 1;//前三项 
    for(int i = 4; i <= 20190324; i++){
        d = (a+b+c)%10000;//控制后四位数 
        a = b;
        b = c;
        c = d;
    }
    printf("%d\n", d);
    return 0;
}

试题 D:数的分解

本题总分:10分
【问题描述】
把 2019分解成 3个各不相同的正整数之和,并且要求每个正整数都不包
含数字2和4,一共有多少种不同的分解方法?
注意交换 3个整数的顺序被视为同一种方法,例如 1000+1001+18和
1001+1000+18被视为同一种。

#include<cstdio>
#include<algorithm> 
#include<iostream>
using namespace std;
bool check(int x)    //判断是否有2,4 
{
    while(x)
    {
        if(x%10==2||x%10==4)
           return true;    
        x/=10;
    }
    return false;
}
int main()
{
    int cnt=0;
    for(int i=1;i<=2019;i++)
    {
        if(check(i)) continue;
        for(int j=i+1;j<=2019;j++)
        {
            if(check(j)) continue;
            int k=2019-i-j;
            if(k<=i||k<=j||check(k))continue;
            cnt+=1;
        }
     } 
     cout<<cnt<<endl;
     return 0;
} 

 

其中i,j,k分别为三个整数,逐个模拟,边模拟边判断是否有数字2和4;

答案:

试题 F: 特别数的和
时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
小明对数位中含有 2、0、1、9 的数字很感兴趣 (不包括前导 0)  ,在 1 到
40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574。
请问,在 1 到 n 中,所有这样的数的和是多少?
【输入格式】
输入一行包含两个整数 n。
【输出格式】
输出一行,包含一个整数,表示满足条件的数的和。
【样例输入】
40
【样例输出】
574
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n ≤ 10。
对于 50% 的评测用例,1 ≤ n ≤ 100。
对于 80% 的评测用例,1 ≤ n ≤ 1000。
对于所有评测用例,1 ≤ n ≤ 10000。

 

#include<cstdio>
#include<algorithm> 
#include<iostream>
using namespace std;
int main()
{
    int n,sum=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        if(i%10==1||i/10==1||i%10==2||i/10==2||i%10==9||i/10==9||i%10==0)
          sum+=i;
    }
    cout<<sum<<endl;
}

 

分析:把运用二进制的特征把同一层的相加即可。

#include<bits/stdc++.h>
using namespace std;
//题号:完全二叉树的权值

int cc[1000];
int main()
{
	ios::sync_with_stdio(false);
	int n ;
	cin >> n;
	int j = 1;//记录当前层 
	for(int i = 1; i<=n; i++)
	{
		int temp;
		cin >> temp;
		if(i >= (1<<j)) j++;//利用二进制控制层数 
		cc[j]+=temp;
	}
	int ans = 1,maxx = cc[1];
	for(int i = 2; i<=j; i++)
	{
		if(cc[i]>maxx)
		{
			ans = i;
			maxx = cc[i];
		}
	}
	
	cout<<ans<<endl;
	return 0;
}

试题 H: 等差数列
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一
部分的数列,只记得其中 N 个整数。
现在给出这 N 个整数,小明想知道包含这 N 个整数的最短的等差数列有
几项?
【输入格式】
输入的第一行包含一个整数 N。
第二行包含 N 个整数 A 1 ,A 2 ,··· ,A N 。(注意 A 1 ∼ A N 并不一定是按等差数
列中的顺序给出)
【输出格式】
输出一个整数表示答案。
【样例输入】
5
2 6 4 10 20
【样例输出】
10
【样例说明】
包含 2、6、4、10、20 的最短的等差数列是 2、4、6、8、10、12、14、16、
18、20。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e5+10;
int w[maxn];
int ans[maxn];
int main(){
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &w[i]);
    }
    if(w[1]==w[2])   //常数列的情况 
    {
        printf("%d", n);
        return 0;
    }
    sort(w+1, w+n+1);//先排序,从小到大 
    for(int i = 1; i < n; i++){
        ans[i] = w[i+1]-w[i];     //依次算出两数之差 
    }
    int G = ans[1];
    for(int i = 2; i <= n-1; i++){
        G = gcd(G, ans[i]);
    }
    printf("%d\n", (w[n]-w[1])/G+1);
    return 0;
} 

解题思路:

从概念上讲,后缀表达式的意义和中缀表达式应该是一样的,想一想熟悉的中缀表达式,我们可以自由定制数字运算的顺序,那么后缀表达式也应该有这种能力,即能随意组合运算顺序,我们知道这个概念就行。第二点是如果只有 + 、- 运算符,那么所有的数字都可以看成是相加的,-运算符可以看成负号。那么题目就可以看成有 N + M + 1 个数字进行相加,但是必须要有 M 个数字变成其本身的相反数,我们很容易想到可以把负数变成它的相反数,就成了正数,顺序应该是先将绝对值最大的负数变成正数,再是其他的数字。我们还需要讨论负数的个数和 M 的关系:1、给定的数字本身中负数的个数小于 M,这种情况下剩下绝对值最小的几个负数。2、给定的数字本身中负数的个数大于 M, 这种情况和 1 相似。3、给定的数字本身中负数的个数等于 M,这种情况全是正数,皆大欢喜。最后做加法就行了

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

const int MAXN = 200020;
int nums[MAXN];

// 自定义排序函数:按绝对值从大到小排序 
bool com(int a, int b) {
	return abs(a) > abs(b);
}

int main() {
	int N, M;
	cin >> N >> M;
	int n  = N + M + 1;
	long long res = 0;
	for (int i = 0; i < n; i++) {
		scanf("%d", nums + i);
	}
	sort(nums, nums + n, com);
	// 将负数变成正数 
	for (int i = 0; i < n && M > 0; i++) {
		if (nums[i] < 0) {
			nums[i] = -nums[i];
			M--;
		}
	}
	// 如果还存在负号,则将最后的数字变成负数 
	if (M) {
		for (int i = n - M; i < n; i++) {
			nums[i] = -nums[i];
		} 
	} 
	// 求和 
	for (int i = 0; i < n; i++) {
		res += nums[i];
	}
	cout << res << endl;
	return 0;
} 

 

 

解题思路:

这个题简单来说就是给定一组数,我们的目标是通过两种操作使得其中的绝对值最大的数达到最小,这两种操作是:
1、如果 a[i] > 0 并且 a[i - 1] 或者 a[i+1] 小于 0,我们可以将 a[i] 借给 i-1 元素和 i+1 元素,同时 a[i] 要变成 -a[i]。
2、如果 a[i] < 0 并且 a[i - 1] 或者 a[i+1] 大于 0,我们可以将 a[i-1] 和 a[i+1] 各借 abs(a[i]) 给 i 元素,之后 i 元素的值变成 -a[i] 也就是正数(a[i] 本身是小于 0 的),同时 a[i - 1] 和 a[i + 1] 要减掉 abs(a[i])。
我们考虑几种情况:
1、所有的数字都为正数或者都为负数,即所有的数字都同号。这种情况是没法借的,因为不符合操作要求,因此这种情况求出数组中绝对值最大的数即可。
2、对于 a[i],如果 a[i] 是正数,并且 a[i - 1] 和 a[i + 1] 至少有一个负数,那么我们的目标就是把那个绝对值最大的负数的绝对值缩小,这个时候如果另一边是正数,则需要考虑操作之后是否会产生新的绝对值更大的正数,比如现在有三个数:5 5 -6,如果我们把中间那个 5 按上面的操作 1 变换之后:10 -5 -1,绝对值最大数变成了 10,而之前是 6,显然不行。如果这三个数是这样的:1 5 -7,那么我们就可以按操作 1 变换:6 -5 -2,最大绝对值从 7 减小到了 6,是可行的。那么变换条件是什么呢?这里假设 a[i - 1] 是正数,a[i + 1] 是负数,那么条件可以写成:a[i - 1] + a[i] < abs(a[i + 1])。而如果 a[i - 1] 和 a[i + 1] 都是负数的时候,当两边的绝对值有一个大于 a[i] 时,就可以进行操作 1 变换。
3、对于 a[i] 是负数的时候,如果 a[i - 1] 是负数, a[i+1] 是正数,那么确保 a[i + 1] > abs(a[i] + a[i-1]),就可以进行操作 2 变换,如果两边都是正数则只要有一边的值大于 abs(a[i]) 时就可以进行操作 2 变换。

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

const int MAXN = 300010;
int nums[MAXN]; 

// 判断 a 和 b 是否异号 
bool judgeYi(int a, int b) {
	return a > 0 && b < 0 || a < 0 && b > 0;
} 

int main() {
	int T, n;
	cin >> T;
	while (T--) {
		cin >> n;
		// 标志负数和正数是否出现 
		bool hasNe = false, hasPo = false;
		int res = 0;
		for (int i = 0; i < n; i++) {
			scanf("%d", &nums[i]);
			if (nums[i] < 0) {
				hasNe = true;
			} else if (nums[i] > 0) {
				hasPo = true;
			}
		}
		// 如果数组中同时存在正负数,则判断能否进行操作 1 和 操作 2 
		if (hasNe && hasPo) {
			bool canNext;
			do {
				canNext = false;
				for (int i = 1; i < n - 1; i++) {
					// nums[i] 和 nums[i-1] 或 nums[i+1] 异号 
					if (judgeYi(nums[i], nums[i-1]) || judgeYi(nums[i], nums[i+1])) {
						if (nums[i] > 0) {
							// nums[i-1] 和 nums[i+1] 异号,
							// 这里的 if 和 else 可以合并,为了逻辑清晰,这里分开写 
							if (judgeYi(nums[i-1], nums[i+1])) {
								if ((nums[i-1] > 0 && abs(nums[i+1]) > nums[i-1] + nums[i]) || 
									(nums[i+1] > 0 && abs(nums[i-1]) > nums[i+1] + nums[i])) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								}
							} else { // nums[i-1] 和 nums[i+1] 同号,都 < 0 
								if (abs(nums[i-1]) > nums[i] || abs(nums[i+1]) > nums[i]) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								} 
							} 
						} else if (nums[i] < 0) {
							// nums[i-1] 和 nums[i+1] 异号 
							if (judgeYi(nums[i-1], nums[i+1])) {
								if ((nums[i-1] > 0 && nums[i-1] > abs(nums[i+1] + nums[i])) || 
									(nums[i+1] > 0 && nums[i+1] > abs(nums[i-1] + nums[i]))) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								}
							} else { // nums[i-1] 和 nums[i+1] 同号,都 > 0 
								if (nums[i-1] > abs(nums[i]) || nums[i+1] > abs(nums[i])) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								} 
							} 
						}
					} 
				}
			} while (canNext);
		}
		int t;
		// 求绝对值最大的值 
		for (int i = 0; i < n; i++) {
			res = max(res, abs(nums[i]));
		}
		cout << res << endl;
	}
	
	return 0;
}

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值