1.格点
问题描述
如果一个点 (x, y)(x,y) 的两维坐标都是整数, 则称这个点为 一个格点。
如果一个点 (x, y)(x,y) 的两维坐标都是正数, 即 x>0 且 y>0, 则称这个点在第一象限。
请问在第一象限的格点中, 有多少个点 (x, y)(x,y) 的两维坐标乘积不超过 2021 , 即x⋅y≤2021 。
提示: 建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
运行限制
最大运行时间:1s
最大运行内存: 256M
解题思路
简单题,数据量也不大,直接暴力枚举。
C++代码
#include<iostream>
using namespace std;
int main()
{
int x, y;
int count= 0;
for (x = 1; x <= 2021; x++)
{
for (y = 1; y <= 2021; y++)
{
if (x * y <= 2021)
count++;
}
}
cout << count;
return 0;
}
2.日期格式
问题描述
小蓝要处理非常多的数据, 其中有一些数据是日期。
在小蓝处理的日期中有两种常用的形式: 英文形式和数字形式。
英文形式采用每个月的英文的前三个宁母作为月份标识, 后面跟两位数字 表示日期, 月份标识第一个字母大写, 后两个字母小写, 日期小于 10 时要补 前导 0s 1 月到 12 月英文的前三个字母分别是 Jan、Feb、Mar、Apr、May、 Jun、Jul、Aug、Sep、Oct、Nov、Dec:
数字形式直接用两个整数表达, 中间用一个空格分隔, 两个整数都不写前导 0。其中月份用 1 至 12 分别表示 1 月到 12 月。
输入一个日期的数字形式, 请输出它的英文形式。
输入格式
输入一行包含两个整数, 分别表示日期的月和日。
输出格式
输出对应的英文形式。
样例输入
2 8
样例输出
Feb08
解题思路
用一个数组存月份的英文形式
用setfill补0,用setw控制输出数字的位数
C++代码
#include<iostream>
#include<iomanip>
#include<string>
using namespace std;
int main()
{
// 请在此输入您的代码
string month[13] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int m, d;
cin >> m >> d;
cout << month[m] << setfill('0') << setw(2) << d;
return 0;
}
3.车牌
问题描述
AA 市的车牌由六位组成, 其中前三位可能为数字 0 至 9 , 或者字母A 至 F, 每位有 16 种可能。后三位只能是数字 0 至 9。为了减少攀比, 车牌中不能有连续三位是相同的字符。
例如, 202020 是合法的车牌, AAA202 不是合法的车牌, 因为前三个字母相同。
请问, A市有多少个合法的车牌?
答案提交
这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
运行限制
最大运行时间:1s
最大运行内存: 256M
解题思路
用一个数组存放车牌的可能情况,0~9和A~F
暴力枚举,六位车牌用六层循环,在最后一层循环时判断是否有连续三个字符相同
C++代码
#include<iostream>
using namespace std;
int main()
{
int count = 0;
char ch1[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', };
for (int a = 0; a < 16; a++)
for (int b = 0; b < 16; b++)
for (int c = 0; c < 16; c++)
for (int d = 0; d < 10; d++)
for (int e = 0; e < 10; e++)
for (int f = 0; f < 10; f++)
if (!((a==b&&b==c) || (b==c&&c==d) || (c==d&&d==e) || (d==e&&e==f)))
count++;
cout << count;
return 0;
}
4.数青蛙
问题描述
“一只青蛙一张嘴, 两只眼睛四条腿。两只青蛙两张嘴, 四只眼睛八条腿。 三只青蛙三张嘴, 六只眼睛十二条腿。 ⋯⋯ 二十只青蛙二十张嘴, 四十只眼睛八十条腿。"
请问上面这段文字, 如果完全不省略, 全部写出来, 从 1 到 20 只青蛙, 总共有多少个汉字。
约定: 数字 2 单独出现读成 "两”, 在其他数里面读成 “二”, 例如 “十二”。 10 读作 “十”, 11 读作 “十一”, 22 读作 “二十二”。
请只计算汉字的个数, 标点符号不计算。
答案提交
这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
运行限制
最大运行时间:1s
最大运行内存: 256M
解题思路
本题只需考虑汉字,可以发现每句话都有10个字是固定不变的(n只青蛙n张嘴,2n只眼睛4n条腿),因此我们只需计算各个数字用多少个汉字表达。
题目只需算到20只青蛙,因此出现的最大数字是80。不难发现,1~10只用一个汉字,11~20以及其他可能出现的整十数(30、40、...)需要两个汉字,其他则需要三个汉字。
C++代码
#include <iostream>
using namespace std;
int main()
{
int num[4] = {1, 1, 2, 4}; //青蛙数量,嘴数量,眼睛数量,腿数量
int count = 0; //数字部分需要的汉字数量
while (num[0] <= 20)
{
for (int i = 0; i < 4; i++)
{
if (num[i] <= 10)
count++;
else if (num[i] <= 20 || num[i] % 10 == 0)
count += 2;
else
count += 3;
}
num[0]++;
num[1]++;
num[2] += 2;
num[3] += 4;
}
cout << count + 200 << endl; //200是不变的汉字部分
return 0;
}
5.双阶乘
问题描述
一个正整数的双阶乘, 表示不超过这个正整数且与它有相同奇偶性的所有 正整数乘积。 n 的双阶乘用 n !! 表示。
例如:
3!! = 3 * 1 = 3
8!! = 8 * 6 * 4 *2 = 384
请问, 2021!! 的最后 5 位 (这里指十进制位) 是多少?
注意: 2021!! = 2021×2019×⋯×5×3×1 。
提示: 建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
运行限制
最大运行时间:1s
最大运行内存: 256M
解题思路
用一个数组int num[5], 只管理最后五位,num[0]表示个位,依次上升,数组初始化为{1, 0, 0, 0, 0}。
i从3开始,num数组的每一位都乘以i,注意处理进位
C++代码
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int num[5];
memset(num, 0, sizeof(num));
num[0] = 1;
for (int i = 3; i <= 2021; i += 2)
{
int temp = 0; //进位
for (int j = 0; j < 5; j++)
{
num[j] = num[j] * i + temp;
if (num[j] < 10) //没有进位
temp = 0;
else
{
temp = num[j] / 10;
num[j] = num[j] % 10;
}
}
}
cout << num[4] << num[3] << num[2] << num[1] << num[0];
return 0;
}
6.乘法表
问题描述
九九乘法表是学习乘法时必须要掌握的。在不同进制数下,需要不同的乘法表。 例如,四进制下的乘法表如下所示:
1*1=1
2*1=2 2*2=10
3*1=3 3*2=12 3*3=21
请注意,乘法表中两个数相乘的顺序必须为样例中所示的顺序,不能随意交换两个乘数。
给定 P,请输出 P 进制下的乘法表。
输入格式
输入一个整数 P。
输出格式
输出 P进制下的乘法表。P 进制中大于等于 10 的数字用大写字母 A、B、C、··· 表示。
样例输入
4
样例输出
1*1=1
2*1=2 2*2=10
3*1=3 3*2=12 3*3=21
解题思路
1.用数组存放0~9和A~Z,打印等号左边的式子时直接找数组下标(0~35)。
2.求相乘结果,先求十进制相乘的结果,然后转为P进制输出
C++代码
#include<iostream>
#include<stack>
#include<string>
#include<cmath>
using namespace std;
string multiply(int i, int j, int p)
{
//将十进制的运算结果转为p进制并返回
int num = i * j;
string result;
stack<int> s;
while (num != 0)
{
s.push(num % p);
num = num / p;
}
while (!s.empty())
{
if (s.top() < 10)
result += (char)('0' + s.top());
else
result += (char)('A' + s.top() - 10);
s.pop();
}
return result;
}
int main()
{
char ch[36] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
int p;
cin >> p;
for (int i = 1; i < p; i++) //i行
{
for (int j = 1; j <= i; j++) //第i行有j个式子
cout << ch[i] << '*' << ch[j] << '=' << multiply(i, j, p) << ' ';
cout << endl;
}
return 0;
}
7.Fibonacci集合
问题描述
小蓝定义了一个 Fibonacci 集合 F, 集合的元素如下定义:
最小的 5 个 Fibonacci 数 1,2,3,5,8 属于集合 F 。
如果一个元素 x 属于 F, 则 3x+2 、5x+3 和 8x+5 都属于集合 FF 。
其他元素都不而于 F 。
请问,这个集合中的第 2020 小元素的值是多少?
答案提交
这是一道结果填空题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
解题思路
定义一个数组num,按顺序存放属于集合F的数,先把初始值1,2,3,5,8存进去
定义一个变量pos,监视当前num数组已经排到了第几小,初始为6
i从9开始每次加一,从小到大一个一个数列举。如果存在一个数x已经在num数组中,并且可以通过3x+2 、5x+3 或 8x+5得到i,则将i加入num数组,并且pos加一。当pos>2020时结束。
C++代码
#include<iostream>
using namespace std;
int main()
{
int num[2021] = {0, 1, 2, 3, 5, 8};
int pos = 6; //当前排到第几小
for (int i = 9; pos <= 2020; i++) //从9开始枚举 ,找满符合条件的2020个就跳出循环
{
for (int j = 1; j < pos; j++)
{
if ((3 * num[j] + 2 == i) || (5 * num[j] + 3 == i) || (8 * num[j] + 5 == i))
{
num[pos++] = i;
break;
}
}
}
cout << num[2020];
return 0;
}
8.排水管道
问题描述
小蓝正在设计一个排水管道的工程,将水从西边一个离地面很高的水库引到东边的地面。
为了项目更有艺术性,小蓝建造了很多高低不同的的柱子用于支撑流水的水槽,柱子之间的间距相同。
当西边的柱子比东边相邻的柱子至少高 1 时,水能正常通过水槽向东流,否则可能出现异常。
但是,小蓝发现施工方建造的柱子是忽高忽低的,不满足西边高东边低的要求。小蓝不得不修改柱子的高度以便水能正常通过水槽。同时,小蓝需要用上所有的柱子,最东边的柱子高度至少为 1,最西边的柱子高度可以为任意高度。每修改一根柱子的高度,需要花费 1 的代价(不管高度改变多少代价都是 1)。
请问,小蓝最少花费多大的代价,可以让水能正常通过所有水槽?在修改时,小蓝只会把柱子的高度修改为整数高度。
输入格式
输入的第一行包含一个整数 n,表示柱子的数量。
第二行包含 n 个整数 h1 , h2 , ···, hn,从西向东表示每根柱子的高度。
输出格式
输出一行包含一个整数,表示答案。
样例输入
6
8 9 5 2 1 1
样例输出
3
解题思路
该题要用到最长上升子序列LIS算法
先将题目输入的序列reverse,改为熟悉的上升序列
本题与传统的LIS模板不同,比如对于1 3 2序列,传统的LIS中1 2可以作为一个上升子序列,但是此题显然不行,因为还得考虑二者之间的位置容得下其他的管道,也就是说每个位置的管道有高度下限(比如第3个位置必须满足高度大于等于2)。
因此,可以做个巧妙的变形,构造num[i] = num[i] - i,如果num[i] < 0,说明高度小于该位置的高度下限,必须做出调整。而一旦我们找到第一个num[i] >= 0的位置,就从这个位置开始找满足题目要求的最长非递减序列(非递减是根据题目需要,比如位置1和2高度分别为3和4,经过num[i] = num[i] - i后都变成了2,但是显然是满足要求的,因此只需要求非递减序列),最后的答案就是管道的数量减去最长非递减序列的长度。
本题数据较大,如果使用O(n^2)的LIS算法会超时,需要使用O(nlogn)的LIS(二分+贪心)才可以AC
C++代码(AC版)
#include<iostream>
#include<algorithm>
using namespace std;
int num[100001]; //柱子高度,升序
int f[100001]; //f[i]=长度为i的子序列结尾最小的值
int main()
{
int n;
cin >> n;
int temp = 0; //满足num[i]>=i的最小i值,即为搜索子序列的起点
for (int i = n; i >= 1; i--)
{
cin >> num[i];
num[i] = num[i] - i;
if (num[i] >= 0)
{
f[1] = num[i];
temp = i;
}
}
if (temp == 0) //没有一个满足num[i]>=i,因此所有管道都需要调整
{
cout << n;
return 0;
}
int len = 1; //当前最长子序列的长度
//LIS
for (int i = temp + 1; i <= n; i++)
{
if (num[i] < 0)
continue;
if (num[i] >= f[len])
f[++len] = num[i];
else
//upper_bound(first, last, value)
//返回指向范围 [first, last) 中首个大于 value 的元素的迭代器,或若找不到这种元素返回 last 。
f[upper_bound(f + 1, f + len + 1, num[i]) - f] = num[i];
}
cout << n - len;
return 0;
}
C++代码(超时版)
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
//求最大上升子序列,并且序列中的数要满足num[i] >= i
int n;
cin >> n;
int num[n+1];
int f[n+1]; //f[i]是以num[i]结尾的满足要求的最大上升子序列大小
//输入的数从后往前存放,改为上升序列
for (int i = n; i >= 1; i--)
{
cin >> num[i];
if (num[i] >= i)//满足num[i] >= i,初始化为它自身长度即为1
f[i] = 1;
else
f[i] = 0;
}
int m = 0;//最大上升子序列的大小
for (int i = 1; i <= n; i++)
{
for (int j = 1; f[i] != 0 && j < i; j++)
{
if (f[j] != 0 && num[j] + i - j - 1 < num[i])
{
f[i] = max(f[i], f[j] + 1);
}
}
if (f[i] > m)
m = f[i];
}
cout << n - m << endl;
return 0;
}
9.城邦
问题描述
小蓝国是一个水上王国, 有 2021 个城邦, 依次编号 1 到 2021。在任意两个城邦之间, 都有一座桥直接连接。
为了庆祝小蓝国的传统节日, 小蓝国政府准备将一部分桥装饰起来。
对于编号为 a 和 b 的两个城邦, 它们之间的桥如果要装饰起来, 需要的费用如下计算: 找到 a 和 b 在十进制下所有不同的数位, 将数位上的数字求和。
例如, 编号为 2021 和 922 两个城邦之间, 千位、百位和个位都不同, 将这些数位上的数字加起来是 (2+0+1)+(0+9+2)=14(2+0+1)+(0+9+2)=14 。注意 922 没有千位, 千位看成 0 。
为了节约开支, 小蓝国政府准备只装饰 2020 座桥, 并且要保证从任意一个 城邦到任意另一个城邦之间可以完全只通过装饰的桥到达。
请问, 小蓝国政府至少要花多少费用才能完成装饰。
提示: 建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
解题思路
最小生成树算法的直接应用,先根据题意求出任意两个城邦之间的费用,然后用prim算法或kruskal算法求最小生成树
C++代码(prim算法)
#include<iostream>
#include<string.h>
#define MAX 2022
using namespace std;
int matrix[MAX][MAX];
int main()
{
int num[MAX][4];
memset(num, 0, sizeof(num));
for (int i = 1; i < MAX; i++)
{
int temp = i;
for (int j = 3; j >= 0; j--)
{
num[i][j] = temp % 10;
temp = temp / 10;
}
}
//带权图
memset(matrix, 0x3f3f3f3f, sizeof(matrix));
for (int i = 1; i < MAX; i++)
{
for (int j = 1; j < MAX; j++)
{
if (i == j)
continue;
int sum = 0;
for (int k = 0; k < 4; k++)
{
if (num[i][k] != num[j][k])
sum += (num[i][k] + num[j][k]);
}
matrix[i][j] = sum;
}
}
//最小生成树prim
int closest[MAX]; //距离哪个点最近
int lowest[MAX]; //最近权值是多少
bool flag[MAX]; //各点是否已加入最小生成树
memset(flag, false, sizeof(flag));
//第一个点先加入
int point = 1; //新加入的点
flag[1] = true;
lowest[1] = 0;
//初始化
for (int i = 2; i < MAX; i++)
{
closest[i] = 1;
lowest[i] = matrix[1][i];
}
int sum = 0;
for (int k = 1; k < MAX - 1; k++)
{
int min = 0x3f3f3f3f; //最近的权值
int minId;
//找最近的顶点
for (int i = 1; i < MAX; i++)
{
if (!flag[i] && matrix[point][i] < min)
{
min = matrix[point][i];
minId = i;
}
}
//加入
flag[minId] = true;
point = minId;
//更新所有未加入生成树的顶点
for (int i = 1; i < MAX; i++)
if (!flag[i] && lowest[i] > matrix[point][i])
{
lowest[i] = matrix[point][i];
closest[i] = point;
}
}
for (int i = 1; i < MAX; i++)
sum += lowest[i];
cout << sum;
return 0;
}
10.画中漂流
问题描述
在梦境中, 你䠖上了一只木筏, 在江上漂流。
根据对当地的了解,你知道在你下游 D 米处有一个㷋谷, 如果你向下游前进大干等于 D米则必死无疑。
现在你打响了急救电话, T 秒后救抜䀒会到达并生你救上岸。水流速度是 1 m/s, 你现在有 M点体力。 每消耗一点体力, 你可以划一秒桨使船向上游前进 1m, 否则会向下游前进 1 m。 M 点体力需在救援队赴来前花光。因为江面太宽了, 凭借你自己的力量不可能上岸。
请问, 有多少种划奖的方案可以让你得救。
两个划桨方案不同是指; 存在某一秒, 一个方案划桨, 另一个方案不划。
输入格式
输入一行包含三个整数 D, T, M
输出格式
输出一个整数, 表示可以让你得救的总方案数, 答案可能很大, 请输出方案数除以 1,000,000,007 的余数。
样例输入
1 6 3
样例输出
5
解题思路
用a[i][j]表示用了i秒消耗j点体力还活着的方案数,所以题目要求的便是a[T][M]。
该题用动态规划,分析可知,当j>0时,第i秒有使用体力和不使用体力两种情况,未使用体力的方案数是a[i-1][j],使用了体力的方案数量是a[i-1][j-1],所以总方案数是两者相加。而当j=0时,则只有不使用体力的情况,方案数为a[i-1][j]。
设置初始值a[0][0] = 1。
C++代码
#include <iostream>
#include<string.h>
using namespace std;
long long a[3000][1500];
int main()
{
int d, t, m;
cin >> d >> t >> m;
//用了i秒j点体力还活着的方案
a[0][0] = 1;
for (int i = 1; i <= t; i++)
{
for (int j = 0; j <= min(i, m); j++)
{
int k = (i - j) - j; //用了j点体力后的坐标
if (k >= d) //超过d,该方案不行
continue;
if (j)
a[i][j] = (a[i - 1][j] + a[i - 1][j - 1]) % 1000000007;
else
a[i][j] = a[i - 1][j] % 1000000007;
}
}
cout << a[t][m];
return 0;
}