试题 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;
}