考试概览
题号 | 名称 |
---|---|
A | 方块消消乐 |
B | 相同的整数 |
C | 数列+1 |
D | 涂颜色 |
E | 外卖店 |
F | 外面路程 |
项目 | 内容 |
---|---|
说明 | 阶段性小练习 |
类别 | 日常作业题目列表 |
考试规则 | 信息学奥赛规则 |
开始时间 | 2023/5/20 14:10:00 |
结束时间 | 2023/5/20 16:10:00 |
总时长 | 2小时 |
参考人数 | 共 43 人进入, 40 人提交 |
提交限制 | 每题不超过 10 次 |
总结+吐槽
第一次线下课,老师何必行此大礼……
题目难度还行,E、F不会。
考了第三名,拿到了老师的奶茶。
第一次限制提交次数,总感觉次数会用完。不过并没有。
A题:刚开始用的模拟,TLE了4个点,后来想出新思路,终于AC了。
B题:真的很简单。
C题:刚开始感觉这道题是自己不擅长的类型,感觉做不出来,写着写着感觉和B好像,然后AC了。
D题:刚开始没读懂题,想到了模拟、骗分,最后想出正解,AC了。我同桌还好奇我是怎么骗到AC的。
E题:感觉和P5224很像,但网课摸鱼(羞),不会,骗了10分。后来发现这其实是 摄像头1模型
F题:不出意外的不会,1分都没骗着: (。
题解
A题:方块消消乐
问题描述
何老板在玩一款消消乐游戏,游戏虽然简单,何老板仍旧乐此不疲。
游戏一开始有n个边长为1的方块叠成一个高为n的柱子。
有红色和蓝色两种方块。
游戏操作:玩家选择两个相邻且不同色的方块,将它们消除。然后上方的方块会自动落下来,使得剩下的方块始终保持柱状。
玩家可以进行任意次上述操作,消除的方块越多,得分越高。何老板想知道:最多能消除掉多少方块?
输入格式
一个由0和1构成的字符串,表示游戏开始时的方块柱子。其中0表示蓝色方块,1表示红色方块。
输出格式
一个整数,表示最多能消除的方块数。
样例
样例输入 1
0011
样例输出 1
4
样例输入 2
11011010001011
样例输出 2
12
样例输入 3
0
样例输出 3
0
提示
设字符串的长度为n:
1
≤
n
≤
1
0
5
1\le n\le 10^5
1≤n≤105
思路及代码
本题我用的是string
,char
字符组也行。
输入就难倒了一大片英雄汉。
在不知道总长度的情况下,怎么输入字符串?
很明显,cin>>
嘛!看到有人还在用for
和while
,我震惊了。
我的第一种思路:模拟
每当找到可以消除的方块对时,消除它们两个,把后面的往前移。
往前移的块数越少,速度越快,因此应从后往前搜索。
72分代码:
#include<bits/stdc++.h>
using namespace std;
string a;
int main(){
int n = 0,ans = 0;
bool flog = 1;//flog用于检测本次循环是否消除方块,若没有可消除的,就不循环了。
cin>>a;
n = a.size();
while(flog){
flog = 0;
for(int i = n-1; i >= 0; i--){
if((a[i] == '1' && a[i-1] == '0') || (a[i] == '0' && a[i-1] == '1')){
ans += 2;
a.erase(i-1,2);
n -= 2;
flog = 1;
break;
}
}
}
printf("%d",ans);
return 0;
}
但——
看来模拟不是正解。
还有一种方法,就是统计红块和蓝块的个数,选择较少的一个颜色,把它们全部消除。答案就是它们的总数
×
2
\times 2
×2。
AC代码:
#include<bits/stdc++.h>
using namespace std;
string a;
int main(){
int n = 0,b = 0,r = 0;
cin>>a;
n = a.size();
for(int i = 0; i < n; i++){
if(a[i] == '1'){
r++;
}else{
b++;
}
}
if(r > b){
printf("%d",b*2);
}else{
printf("%d",r*2);
}
return 0;
}
B题:相同的整数
问题描述
有三个整数 A A A、 B B B和 C C C,有以下两种操作可供你选择:
从
A
A
A、
B
B
B、
C
C
C中任选两个数,使它们都增加
1
1
1
从
A
A
A、
B
B
B、
C
C
C中任选一个数,使它增加
2
2
2
请问如果想让
A
A
A、
B
B
B、
C
C
C都相等,最小需要操作几次。
输入格式
第一行,三个空格间隔的整数 A A A、 B B B、 C C C。
- 0 ≤ A , B , C ≤ 50 0\le A,B,C\le 50 0≤A,B,C≤50
- A A A、 B B B、 C C C均为整数。
输出格式
一个整数,表示最小需要操作的次数。
样例
样例输入 1
2 5 4
样例输出 1
2
样例输入 2
2 6 3
样例输出 2
5
样例输入 3
31 41 5
样例输出 3
23
提示
样例一:
- 第一步,将 2 2 2和 4 4 4都增加1,得到 3 3 3、 5 5 5、 5 5 5。
- 第二步,将 3 3 3增加 2 2 2,得到 5 5 5、 5 5 5、 5 5 5。
思路及代码
我们先将3个数从小到大排序;
接下来算出第1、2个数与最大的数的差距总和。
接下来分情况讨论:
- 如果这个差距总和是偶数,那么不管用哪种操作,差距总和都会减2。总操作数为差距总和 ÷ 2 \div 2 ÷2。
- 如果这个差距总和是奇数,我们可以将最大数和另一个数分别加1,剩下的那个数不变。差距总和加1,变为偶数。接下来按情况1操作。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int a[3];
int main(){
int x,y;
for(int i = 0; i < 3; i++){
scanf("%d",&a[i]);
}
sort(a,a+3);
x = a[2]-a[0];
y = a[2]-a[1];
if((x-y)%2 == 1){
printf("%d",(x+y+1)/2+1);
}else{
printf("%d",(x+y)/2);
}
return 0;
}
C题:数列+1
问题描述
给n个整数,每次操作可以将⼀个数+1,要使这n个数都不相同, 求⾄少要加多少次?
n<=10000 给出的数字数值在[-10000,10000]
输入格式
第一行,一个整数n
第二行,n个空格间隔的整数
输出格式
一个整数,表示最少需要操作的次数
样例
样例输入 1
4
1 3 1 4
样例输出 1
1
样例输入 2
5
1 2 3 2 5
样例输出 2
2
思路及代码
我用的是桶排序的思想。
已知给出的数字数值在[-10000,10000]之间,一共20001个数。
每输入一个数,就看看该位置有没有数。如果有数,就不停+1直到到达没有数的位置。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int a[20001];
int main(){
int n,tp,ans = 0;
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%d",&tp);
a[tp+10000]++;
if(a[tp+10000] >= 2){
for(int i = tp+10000; a[i] >= 2; i++){
ans++;
a[i] -= 1;
a[i+1]++;
}
}
}
printf("%d",ans);
return 0;
}
D题:涂颜色
问题描述
有n堆石头,第i堆的石头数量为Ai。
何老板有k种颜色的颜料,他安排你给所有石头都涂上颜色。
何老板还给你提出一个额外的要求:任意两堆石头中,颜色相同的石头的数量之差不能超过1。
在涂色前,你需要先回答何老板是否存在满足上述条件的方案,能输出“YES”,否则输出“NO”
输入格式
第一行,一个整数t,表示有t组测试数据,对于每组数据:
第一行,两个整数n和k
第二行,n个空格间隔的整数,依次表示每堆石头的数量
输出格式
有解输出YES,无解输出NO
样例
样例输入
3
4 4
1 2 3 4
5 2
3 2 4 1 3
5 4
3 2 4 3 5
样例输出
YES
NO
YES
提示
第3组数据说明:
5堆石头中每颗石头涂色方案如下:
第1堆:1 2 3
第2堆:1 3
第3堆:1 2 3 4
第4堆:1 3 4
第5堆:1 1 2 3 4
数据范围:
1 ≤ t ≤ 10
1 ≤ n, k ≤ 100
1 ≤ 每堆石头数量 ≤ 100
思路及代码
先排序,找到最少的石头堆,设这堆石头有
x
x
x个。
将每堆石头前
x
x
x个涂上相同的颜色。
剩下的石头每一个涂一种颜色(如图):
这下就好办了。我们只需要算出最大的石头与最小的石头之差,看这个差是否大于颜料总数即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int a[120];
int main(){
int t,n,k;
scanf("%d",&t);
for(int q = 0; q < t; q++){
scanf("%d%d",&n,&k);
memset(a,0,120);//清零
for(int i = 0; i < n; i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
if(a[n-1]-a[0] <= k){
printf("YES\n");
}else{
printf("NO\n");
}
}
return 0;
}
E题:外卖店
问题描述
有一条笔直公路,何老板打算在公路旁开若干间快餐店,以便为沿路的 n n n个小区提供外卖服务。
有个
n
n
n小区,全部分布在公路的同一侧。为保证送餐速度以及用餐品质,何老板规定,外卖店送餐的直线距离不能超过
d
d
d米。
现在已知每个小区的位置,问,最少开设多少间快餐店,就能给所有小区提供外卖服务。 若无法给所有
n
n
n个小区都送餐,输出
−
1
-1
−1。
具体而言,如下图所示,给出一个坐标轴,其中
x
x
x轴表示笔直公路,何老板的快餐店都将开设在
x
x
x轴上。
所有的小区都分布在
x
x
x轴上方(即
y
y
y轴的正半轴)。每个外卖店服务的半径为
d
d
d,问最少需要多少间外卖店。
输入格式
第一行,两个整数
n
n
n和
d
d
d
接下来
n
n
n行,每行两个整数
x
x
x,
y
y
y,表示一个小区的坐标
输出格式
一行,一个整数,表示最少所需外卖店数量。无解输出" − 1 -1 −1"
样例
样例输入 1
3 2
1 2
-3 1
2 1
样例输出 1
2
样例输入 2
8 5
2 4
-4 4
-3 3
-3 1
-3 0
-1 0
0 5
6 0
样例输出 2
3
提示
1
≤
n
≤
50000
1\le n\le 50000
1≤n≤50000
1
≤
d
≤
50000
1\le d\le 50000
1≤d≤50000
−
50000
≤
x
,
y
≤
50000
-50000\le x,y\le 50000
−50000≤x,y≤50000
样例1解释:如题图所示
思路及代码
思路:这本质上是摄像头1模型。如何将每个外卖店转换成线段呢?很简单。
对于每个外卖店,我们知道它的
y
y
y坐标和最远距离
d
d
d。那么我们求得
f
f
f为
d
2
−
y
2
\sqrt{d^2-y^2}
d2−y2。
那么此时端点
a
a
a就是
x
−
f
x-f
x−f,端点
b
b
b为
x
+
f
x+f
x+f。于是乎,这道题就变成了标准的摄像头1模型。
注意判定无解情况,即
y
>
d
y > d
y>d。
AC代码:
#include<bits/stdc++.h>
using namespace std;
struct xiaoqu{
int x,y;
double a,b;
}x[50005];
bool cmp(xiaoqu a,xiaoqu b){
return a.b < b.b;
}
int main(){
int n,d,first = 0,ans = 1;
double f;
bool go_on = 1;
scanf("%d%d",&n,&d);
for(int i = 0; i < n; i++){
scanf("%d%d",&x[i].x,&x[i].y);
if(x[i].y > d){
printf("-1");
go_on = 0;
break;
}else{
f = sqrt((d*d) - (x[i].y*x[i].y));
x[i].a = x[i].x-f;
x[i].b = x[i].x+f;
}
}
if(go_on){
sort(x,x+n,cmp);
for(int i = 0; i < n; i++){
if(x[first].b < x[i].a){
ans++;
first = i;
}
}
printf("%d",ans);
}
return 0;
}
F题:外卖路程
问题描述
何老板下班回家,他骑着一辆自行车,学校到家的为一条直路,距离为M公里。学校在0公里处,家在M公里处。
沿途每个一公里,都有一个小区。小区间有大量的外卖业务。何老板打算在回家途中,顺便揽送一些送外卖,赚点钱,补贴家用。
今天,何老板揽了N个送外卖业务,其中第i号业务用两个整数 a i , b i a_i,b_i ai,bi来表示,何老板需要在第 a i a_i ai公里处的小区去取外卖,送到 b i b_i bi公里处的小区。
何老板的自行车上有个足够大的货框,能装下所有外卖。问,送完所有外卖,最后回到家,何老板行驶的最短总路程是多少?请你帮忙计算!
输入格式
第一行,两个整数n和m
接下来n行,每行两个整数,表示一单业务的起点和终点
输出格式
一个整数,表示最小总距离
样例
样例输入 1
2 10
2 8
6 4
样例输出 1
14
样例输入 2
8 15
1 12
3 1
3 9
4 2
7 13
12 11
14 11
14 13
样例输出 2
27
提示
对于40%的数据,
n
≤
5000
n\le 5000
n≤5000
对于50%的数据,
n
≤
200000
n\le 200000
n≤200000
对于100%的数据,
n
≤
300000
n\le 300000
n≤300000,
3
≤
m
≤
1
0
9
3\le m\le 10^9
3≤m≤109
思路及代码
总路程分为两段:
- 回家固定的路程: m m m。
- 额外的路程:送外卖的部分。
对于每一个订单:
- a < b a<b a<b则顺路,不需要额外的往返路程;
- a > b a>b a>b则不顺路,需要额外的往返路程。
AC代码:
#include<bits/stdc++.h>
using namespace std;
struct xiaoqu{
int a,b;
}a[3000005];
inline bool operator < (xiaoqu a,xiaoqu b){
return a.b < b.b;
}
int main(){
int n,m,l = 0,r = 0;
long long int ans;
scanf("%d%d",&n,&m);
ans = m;
for(int i = 0; i < n; i++){
scanf("%d%d",&a[i].a,&a[i].b);
}
sort(a,a+n);
for(int i = 0; i < n; i++){
if(a[i].a <= a[i].b){
continue;
}
if(a[i].b > r){
ans += 2*(r-l);
l = a[i].b;
}
r = max(r,a[i].a);
}
ans += 2*(r-l);
printf("%lld ",ans);
return 0;
}
记得三连。