✅ 比赛前练练手的… 能拿下多少分是多少分
文章目录
一、空间——[5 分]
1.1 解析
● 小蓝准备用 256MB
的 内存空间
开了一个数组,即这个数组的 内存空间
是 256 × 1024 × 1024 B
,也即是 256 × 1024 × 1024 × 8 bit
。
● 而数组的每个元素都是 32
位,即 32 bit
。
● 如果不考虑程序占用的空间和维护内存需要的辅助空间,则 256MB
的空间可以存储多少个 32 bit
的数据呢?(题目中的 “二进制整数” 是干扰信息,不用多想)
● 即用 256 × 1024 × 1024 × 8 bit
除以 32 bit
即可。
1.2 答案
● 填空题答案:67108864
二、卡片——[5 分]
2.1 解析
● 这道题是一道打卡 模拟题。
● 初始化:用一个长度为 10
的数组来装 0 ~ 9
这 10
种卡片,然后分别赋值 2021
。
● 接着 start_num
赋值为 1
,开始从 1
处理,处理完后执行 start_num++
。
● 对每一个出现的 start_num
,灵活运用 %
和 \
来分解一个数。比如将 703
分解为 7、0、3
。
● 注意细节:为什么在我的代码里,最后的答案要 -1
,因为当 cnt[end] < 0
条件满足时,就已经说明这个 数
不能够用现有的卡片凑成了。故要 -1
。
2.2 答案
● 填空题答案:3181
● 计算机编程C++代码(参考):
#include<iostream>
#include<stdlib.h>
using namespace std;
int cnt[10];
int main()
{
int start_num = 1, flag = 1;
for(int i = 0; i < 10; i++)
{
cnt[i] = 2021;
}
while(flag)
{
int tmp = start_num;
while(tmp > 0)
{
int end = tmp % 10; // 取出这个数的个位
cnt[end]--;
if( cnt[end] < 0 ) // 卡片不够用了
{
flag = 0;
cout << "跳出循环已执行,且答案=" << start_num - 1 << endl;
break;
}
tmp = tmp / 10;
}
start_num++;
}
system("pause");
return 0;
}
三、直线——[10 分]
3.1 解析
● 这道题也是一道 模拟题。
● 依次遍历 “第一个点 A ( x 1 , y 1 ) (x1,y1) (x1,y1)” 和 “第二个点 B ( x 2 , y 2 ) (x2,y2) (x2,y2)” 即可。其中 x ∈ [ 0 , 19 ] , y ∈ [ 0 , 20 ] x∈[0,19],y∈[0,20] x∈[0,19],y∈[0,20]
● 在遍历中,每生成一对新的 A 和 B,我们要去计算连接 A 和 B 的直线的 斜率k 和 截距b。如果得到的这一对 (k,b) 从未出现过,则答案数 +1
,否则跳过忽略开始遍历下一对新的 A 和 B。
● 由高中知识可知: 若 y = k × x + b , 则 k = y 1 − y 2 x 1 − x 2 , b = y 1 − k × x 1 若 y=k\times x + b,则k = \frac{y1-y2}{x1-x2},b=y1-k\times x1 若y=k×x+b,则k=x1−x2y1−y2,b=y1−k×x1
● 然而,如果直接用 b=y1-k*x
来计算 b
,会导致精度爆炸(即溢出)。故我们要进行简单地推导: b = y 1 − k × x 1 = y 1 − y 1 − y 2 x 1 − x 2 × x 1 = x 1 y 1 − x 2 y 1 − ( x 1 y 1 − x 1 y 2 ) x 1 − x 2 = x 1 y 2 − x 2 y 1 x 1 − x 2 b=y1-k\times x1=y1-\frac{y1-y2}{x1-x2} \times x1=\frac{x1y1-x2y1 -(x1y1-x1y2)}{x1-x2}=\frac{x1y2-x2y1}{x1-x2} b=y1−k×x1=y1−x1−x2y1−y2×x1=x1−x2x1y1−x2y1−(x1y1−x1y2)=x1−x2x1y2−x2y1
● 注意:存在 “竖直直线” 的情况,我们单独对其进行判断即可。
● 关于 “去重”,我们用 C++ 的 “集合set
”,它自带去重功能。
3.2 答案
● 填空题答案:40257
#include<iostream>
#include<stdlib.h>
#include<set>
using namespace std;
set< pair<double,double> > s1; // 装一般情况(非竖直)的直线
set<int> s2; // 装竖直的直线
int main()
{
for(int x1 = 0; x1 < 20; x1++)
{
for(int y1 = 0; y1 < 21; y1++)
{
for(int x2 = 0; x2 < 20; x2++)
{
for(int y2 = 0; y2 < 21; y2++)
{
if(x1 == x2) // 说明是一条竖线
{
s2.insert(x1);
}
else
{
double k = 1.0 * (y1 - y2) / (x1 - x2); // 斜率
double b = 1.0 * (x1 * y2 - x2 * y1) / (x1 -x2); // 截距
s1.insert({
k,b});
}
}
}
}
}
cout << "答案为:" << s1.size() + s2.size();
system("pause");
return 0;
}
四、货物摆放——[10 分]
4.1 解析
● 我感觉我做复杂了。但总归做出来了,花了 1
个小时左右吧。
● 网上有巧妙的解法,直接穷举,但要讲究效率,因为题目中的 n
很大,2021041820210418
这么大,穷举得不好的话,一天都算不完。
● 我的方法:假设 n=24
,那么我只向下分解两层 质因数对,注意是 “对”,分解成一对一对的。显然 24
可以分解成 (2,12)
、 (3,8)
、 (4,6)
,那么 (2,12)
就和它上一层左边的 1
构成 (1,2,12)
就是一个排列方式,然后对其进行全排列,即可得到 6
种排列方式。
而对于 (3,8)
也是一样大,它和上一层左边的 1
构成 (1,3,8)
也是一个排列方式,然后对其进行全排列,又得到 6
种排列方式。
然后对于 “第2
层”,12
分解成 (2,6)
时,那么 (2,6)
就和上一层左边的 2
构成 (2,2,6)
也是一个排列方式,然后对其进行全排列,又得到 3
种排列方式。
在分解过程中要注意去重,这里我用的 c++ 的 集合set
。
比如说,我为什么说第2
层的 (2,4)
是重复的,因为它和上一层左边的 3
构成 (3,2,4)
本质上和 “第2
层的 (3,4)
和上一层左边的 2
构成 (2,3,4)
” 是一样的。
4.2 答案
● c++代码:
#include<iostream>
#include<stdlib.h>
#include<queue>
#include<set>
#include<algorithm>
using namespace std;
#define lld long long int
set< pair< pair<lld,lld>, lld> > s1;
queue< pair<lld,lld> > q1;
lld Calculate(lld x, lld y, lld z) // 计算 x, y, z 的全排列有 ? 种
{
if(x == y && x == z)
return 1;
else if(x == y || x == z || y == z)
return 3; // 其中有两个相同
else
return 6; // 三个数都不一样
}
lld mid(lld x, lld y, lld z) // 计算三个数中, 值在中间的那个数
{
if(y <= x && x <= z || z <= x && x <= y)
return x;
else if(x <= y && y <= z || z <= y && y <= x)
return y;
else if(x <= z && z <= y || y <= z && z <= x)
return z;
}
int main()
{
lld n = 2021041820210418;
lld ans = 0; // 装答案
int cnt = 2; // 下沉次数
q1.push({
1,n});
while( cnt > 0 )
{
cnt--;
queue< pair<lld,lld> > q2; // 临时队列
for(lld i = 0; i < q1.size(); i++)
{
pair<lld,lld> tmp = q1.front();
lld num_1 = tmp.first; // 取出第一个数
lld num_2 = tmp.second; // 取出第二个数
q1.pop();
// 计算因数(除开1)
for(lld j = 2; j * j <= num_1; j++)
{
if(num_1 % j == 0 && num_1 / j != 1) // 没有余数, 即能整除(且商不为1)
{
lld shang = num_1 / j; // 取出商
q2.push({
j, shang});
lld min_num = min(min(j,shang),num_2);
lld mid_num = mid(j,shang,num_2);
lld max_num = max(max(j,shang),num_2);
pair< pair<lld,lld>, lld> p = make_pair( make_pair(min_num,mid_num), max_num);
s1.insert(p);
}
}
// 计算因数(除开1)
for(lld j = 2; j * j <= num_2; j++)
{
if(num_2 % j == 0 && num_2 / j != 1) // 没有余数, 即能整除(且商不为1)
{
lld shang = num_2 / j; // 取出商
q2.push({
j, shang});
lld min_num = min(min(j,shang),num_1);
lld mid_num = mid(j,shang,num_1);
lld max_num = max(max(j,shang),num_1);
pair< pair<lld,lld>, lld> p = make_pair( make_pair(min_num,mid_num), max_num);
s1.insert(p);
}
}
}
q1 = q2;
}
set< pair< pair<lld,lld>, lld> >::iterator iter;
for(iter = s1.begin(); iter != s1.end(); iter++)
{
pair<lld,lld> p = (*iter).first;
lld num1 = p.first;
lld num2 = p.second;
lld num3 = (*iter).second;
ans += Calculate(num1,num2,num3);
}
cout << "答案为:" << ans + 3; // 最后要考虑 (1,1,n) 这种全排列的情况
system("pause");
return 0;
}
五、路径——[15 分]
5.1 解析
● 大概是用 最小生成树+辗转相除法
吧…没时间做了
5.1 答案
● 后面有空补充吧…
六、时间显示——[15 分]
◆ 补充:【样例输出 2】01:08:23
6.1 解析
● 理解清楚题意后,善用 \
和 %
来依次处理输入的 time
,以便得到相应的 时、分、秒
即可。
● 另外,这道题目的小难点在于 “格式化输入输出”,即怎么打印出 01:08:23
,而不是 1:8:23
。
● 复习一下 C 语言的 “格式化输出字符” 即可知道:
输出格式 | 功能 |
---|---|
%3d |
输出宽度为 3 位,如果不足 3 位,前面空格补齐(即右对齐) |
%05d |
输出宽度为 5 位,如果不足 5 位,前面 0 补齐 |
%-4d |
输出宽度为 4 位,如果不足 4 位,后面空格补齐(即左对齐) |
%.2f |
小数点后只保留 2 位小数。 |
%6.3f |
输出宽度为 6 位,且小数点后只保留 2 位小数。 |
… | … |
6.1 答案
#include<iostream>
#include<cstdio>
using namespace std;
#define lld long long int
int main()
{
lld t;
cin >> t;
t = t / 1000; // 去掉毫秒
t = t % (3600 * 24); // 只去最后一天的时间
int hour = t / 3600;
int minute = t % 3600 / 60;
int second = t % 3600 % 60;
printf("%02d:%02d:%02d",hour,minute,second);
return 0;
}
七、砝码称重——[20 分]
7.1 解析
● 集合set
具有 去重 功能,做这道题很适合。
● 集合s1
作为一个 “答案容器”,集合s2
作为一个 “临时容器”。详细过程见代码。
7.1 答案
● c++代码:
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define lld long long int
set<int> s1;
int n;
lld ans;
int w;
int main()
{
cin >> n;
s1.insert(0); // 一开始需要放一个 “重量为0的砝码”, 对于例题而言: 以便后续凑出 0 + 6 的情况
set<int>::iterator iter;
for(int i = 0; i < n; i++)
{
cin >> w;
set<int> s2;
for(iter = s1.begin(); iter != s1.end(); iter++)
{
int tmp = *iter;
s2.insert(tmp + w); // 砝码放在 左边
s2.insert(abs(tmp - w)); // 砝码放在 右边
}
for(iter = s2.begin(); iter != s2.end(); iter++)
{
s1.insert( (*iter) ); // 把 s2 中 “新凑出来的重量” 全部装入 s1
}
}
cout << s1.size() - 1; // 最后要把 “重量为0的答案” 去掉
return 0;
}
八、杨辉三角形——[20分]
补充:【评测用例规模与约定】对于 20%
的评测用例,1 ≤ N ≤ 10
;对于所有评测用例,1 ≤ N ≤ 1000000000
。
8.1 解析
● 杨辉三角形,根据上图来找规律,然后用穷举法:用两个 vector
来模拟。但只能得一部分的分。
8.2 答案
● 只能获得 40
分的答案:【满分答案有点难想】
#include<iostream>
#include<vector>
using namespace std;
typedef long long int lld;
vector<lld> v1;
lld n;
lld ans;
int main()
{
cin >> n;
if(n == 1)
{
cout << 1;
return 0;
}
ans++;
v1.push_back(1);
while(1)
{
vector<lld> v2;
for(lld i = 0; i < v1.size(); i++)
{
if(i == 0)
{
ans++;
v2.push_back(1);
}
else
{
ans++;
if(n == v1[i-1]+v1[i]) // 如果符合要求
{
cout << ans;
return 0;
}
v2.push_back(v1[i-1]+v1[i]);
}
}
ans++;
v2.push_back(1);
v1 = v2;
}
return 0;
}
九、双向排序——[25分]
9.1 解析
● 没时间了… 用暴力的 sort
能凑合 60
分吧…
9.2 答案
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,p,q;
int a[100005];
int cmp(int x, int y)
{
return x > y;
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i++)
{
a[i] = i+1;
}
while( m > 0 )
{
m--;
cin >> p >> q;
if(p == 0)
{
sort(a, a+q, cmp); // 逆排
}
else
{
sort(a+q-1, a+n); // 正排
}
}
for(int i = 0; i < n; i++)
cout << a[i] << " ";
return 0;
}
十、括号序列——[25分]
● 没时间了… 不写了…