2023年8月7日普及组南外集训题解
A 坐车
题目描述
有辆公交车,它可以承载35人,包括一个司机。车子最后一排有4个座位,其他10排都有3个座位。每个上车的人,都会先往左边坐(司机所在的那侧)。现在告诉你,有多少人在车上,绘制车内情况。
输入格式
一个整数 \(n\) ,表示有多少个人在车上。\((1≤n≤34)\)
输出格式
输出车内情况,用D表示司机,O表示有人坐,#表示位置上没有人。 具体可以看样例。
样例1
输入
9
输出
+------------------------+
|O.O.O.#.#.#.#.#.#.#.#.|D|)
|O.O.O.#.#.#.#.#.#.#.#.|.|
|O.......................|
|O.O.#.#.#.#.#.#.#.#.#.|.|)
+------------------------+
题解
我们可以用一个二维字符数组来存储初始状态,这样方便我们最后的输出,从样例中可以发现只有最后一列是特殊的,其他的除以三就行了
ac代码
#include <iostream>
using namespace std;
const int N = 35;
char s[50][50] = {
"+------------------------+", "|#.#.#.#.#.#.#.#.#.#.#.|D|)", "|#.#.#.#.#.#.#.#.#.#.#.|.|",
"|#.......................|", "|#.#.#.#.#.#.#.#.#.#.#.|.|)", "+------------------------+"
};
int main() {
int n;
cin >> n;
for (int i = 1; i <= 11; i++) {
for (int j = 1; j <= 4; j++) {
if (i != 1 && j == 3)
continue;
if (n)
s[j][2 * i - 1] = 'O', n--;
}
}
for (int i = 0; i < 6; i++) puts(s[i]);
}
B 猜数字
题目描述
猜数字游戏啦!给你如下四种提示:
(1)这个数严格大于x吗?
(2)这个数严格小于x吗?
(3)这个数大于等于x吗?
(4)这个数小于等于x吗?
每个提示,都会给出相应的答案,yes或者no。
如果有多个数满足条件,输出最小的。如果不存在这样的数,输出“Impossible”。
输入格式
第一行输入一个整数 \(n\) 。
接下来 \(n\) 行,每行一个字符串“sign x answer”,是四个提示的中的一个。
sign是“>”,"<","<=",">="; answer 是“Y”或者"N" ;
如(1)就会有类似这样的字符串“ > x Y” 或者“ > x N”;
输出格式
如果最终的答案有下界的,则输出这个下界
如果存在答案,但是答案没有下界,输出-2000000000
否则输出“Impossible”
样例1
输入
4
>= 1 Y
< 3 N
<= -3 N
> 55 N
输出
3
样例2
输入
2
> 100 Y
< -100 Y
输出
Impossible
题解
非常简单的分支结构,开始时记得定义好上下界就行了
ac代码
#include <iostream>
using namespace std;
int n, o;
int maxx, minn, up, down;
int main() {
cin >> n;
minn = down = -2000000000;
maxx = up = 2000000000;
for (int i = 0; i < n; i++) {
char sign[2], flag;
int a;
cin >> sign >> a >> flag;
if (flag == 'Y' && sign[0] == '>') {
if (sign[1] == '=')
minn = max(minn, a);
else
minn = max(minn, a + 1);
} else if (flag == 'N' && sign[0] == '>') {
if (sign[1] == '=')
maxx = min(maxx, a - 1);
else
maxx = min(maxx, a);
} else if (flag == 'Y' && sign[0] == '<') {
if (sign[1] == '=')
maxx = min(maxx, a);
else
maxx = min(maxx, a - 1);
} else if (flag == 'N' && sign[0] == '<') {
if (sign[1] == '=')
minn = max(minn, a + 1);
else
minn = max(minn, a);
}
}
if (maxx >= minn)
cout << minn << '\n';
else
cout << "Impossible\n";
return 0;
}
C IwantMoreAC
题目描述
学完Fibonacci序列,YY觉得数字太单调了,她打算用 \(S_n=S_{n-2}+S_{n-1}\) 的递推式去操作字符串。如果 \(S_0=AC\) ,\(S_1=ACB\),则 \(S_2=ACACB\)。现在YY有两个字符串 \(S_0\) 和 \(S_1\),她想知道,\(S_n\) 中包含多少个"AC"串。
输入格式
第一行两个字符串 \(S_0\) 、\(S_1\) 和一个数字 \(n\)。
输出格式
输出中包含多少个"AC"
样例1
输入
AC AC 3
输出
3
样例2
输入
ACA CB 2
输出
2
数据范围与提示
样例一中 \(S_3\) =”ACACAC”,所以有3个AC
对于30%的数据,\(S_0\) 和 \(S_1\) 的长度不超过 \(5\),\(n\) 不超过 \(5\)
对于60%的数据,\(S_0\) 和 \(S_1\)和首尾都不包含字母‘A’和‘C'。
对于100%的数据,\(S_0\) 和 \(S_1\)由大写字母构成,长度不超过 \(10\),\(n\) 不超过 \(40\)。
题解
用递推算出最终串是不行的,会超时,其实只需要看两个串拼接后会不会产生新的 “AC” 就行了
ac 代码
#include <iostream>
#include <cstring>
using namespace std;
string s0, s1;
int n, cnt2, cnt1, cnt;
int main() {
cin >> s0 >> s1 >> n;
for (int i = 1; i < s0.size(); i++)
if (s0[i] == 'C' && s0[i - 1] == 'A')
cnt++;
for (int i = 1; i < s1.size(); i++)
if (s1[i] == 'C' && s1[i - 1] == 'A')
cnt1++;
char a = s0[0], b = s0[s0.size() - 1], c = s1[0], d = s1[s1.size() - 1];
for (int i = 1; i < n; i++) {
cnt2 = cnt + cnt1;
if (b == 'A' && c == 'C')
cnt2++;
swap(a, c);
b = d;
cnt = cnt1;
cnt1 = cnt2;
}
cout << cnt2;
return 0;
}
D 体检
题目描述
小王考入了大学,开学前需要去医院体检。
体检一共有 \(N\) 个项目,每个项目检查处都排起了队伍,并且人越来越多。
他一眼就发现,对于第 \(i\) 个项目,如果立即去排队,需要 \(a_i\) 秒才能完成;
如果先做其他检查,在秒后再来,那么要再多等上 \(b_i×t\) 秒。
请你帮他规划,如何安排个项目的顺序,才能尽早完成所有检查。
输入格式
第一行,整数 \(N\) \((1\leq N\leq10^5)\)。
下面 \(N\) 行,每行两个整数 \(a,b\)。
输出格式
一个整数,表示最早完成检查的耗时.(答案对31536000取模)
样例1
输入
5
1 2
2 3
3 4
4 5
5 6
输出
1419
题解
我们可以贪心中的相邻交换思想
第一种:先 \(i\) 再 \(j\) 式子为: \(t+t×b[i]+a[i]+(t+a[i])×b[j]+a[j]\)
第二种:先 \(j\) 再 \(i\) 式子为: \(t+t×b[j]+a[j]+(t+a[j])×b[i]+a[i]\)
假设第一个式子为 \(f(i,j)\) 第二个式子为 \(f(j,i)\)
那么我们就可以得到 \(f(i,j)-f(j,i)=a[i]×b[j]-a[j]×b[i]\)
我们就以此进行排序小的在前,大的在后
注意要开 long long ,并且答案算一次模一次,否则会溢出
ac代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100005, P = 31536000;
int n;
LL ans;
struct node {
LL a, b;
} a[N];
bool cmp(node x, node y) { return (x.a * y.b) < (y.a * x.b); }
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i].a >> a[i].b;
sort(a, a + n, cmp);
for (int i = 0; i < n; i++) {
ans = (ans + a[i].a + ans * a[i].b) % P;
}
cout << ans;
return 0;
}
E 推箱子
题目描述
推箱子应该算是一个家喻户晓的游戏了,这次你的任务就是用电脑算出如何移动人物,完成推箱子。
为了使问题简化,数据中只有3个箱子和3个洞。
注:人一次只能推动一个箱子,箱子推到洞上后不会消失,还可以被推出去,只有当三个箱子同时在洞中时,游戏结束
输入格式
第一行两个整数n和m,表示地图的大小
接下来n行,每行m个元素。
"."表示空地,"#"表示墙
"*"表示箱子
"@"表示洞
"X"表示人
输出格式
输出最少的步数完成游戏,如果无法完成游戏输出-1.
样例1
输入
4 4
....
..*@
..*@
.X*@
输出
7
样例2
输入
6 4
@@@#
#*.#
#X.#
.**.
...#
..##
输出
22
样例3
输入
6 6
...#@.
@..*..
#*##..
..##*#
..X...
.@#...
输出
11
题解
首先,我们不需要存入整张地图,那样会导致内存过大,我们可以只存人的位置,以及三个箱子的位置
因为需要判重的原因,所以数据大概有8的8次方,使用8维的数组来判重
先枚举人的位置,如果遇到了箱子,那就让箱子跟着人移动,不过要注意,箱子要比人往前一个身位,并且不能重叠或出界,具体可以看代码的注释
ac代码
#include <iostream>
using namespace std;
// 8^8 = 16777216
const int N = 17000000, M = 10;
bool st[M][M][M][M][M][M][M][M] = { 0 }; //人、三个箱子的 (x,y)
char mp[M][M];
int dx[4] = { 1, 0, -1, 0 }, dy[4] = { 0, 1, 0, -1 };
int n, m;
struct node {
int step, x[4], y[4]; //步数、三个箱子的 (x,y)
} q[N];
//判断是否出界以及是否能走
bool isoutside(int i, int j) {
if (i > 0 && i <= n && j > 0 && j <= m && mp[i][j] != '#')
return true;
return false;
}
//判重
bool isrepeat(node t) { return st[t.x[0]][t.y[0]][t.x[1]][t.y[1]][t.x[2]][t.y[2]][t.x[3]][t.y[3]]; }
//记录重复
void recordrepeat(node t) { st[t.x[0]][t.y[0]][t.x[1]][t.y[1]][t.x[2]][t.y[2]][t.x[3]][t.y[3]] = true; }
int bfs() {
int hh = 0, tt = 0;
while (hh <= tt) {
node a = q[hh++];
for (int i = 0; i < 4; i++) {
node t = a;
t.x[0] += dx[i];
t.y[0] += dy[i]; //人朝四个方向移动
if (!isoutside(t.x[0], t.y[0]))
continue;
bool flag = false; //记录结果是否合法
for (int j = 1; j < 4; j++) {
// 人与箱子重叠
if (t.x[0] == t.x[j] && t.y[0] == t.y[j]) {
t.x[j] += dx[i];
t.y[j] += dy[i]; //箱子比人要前一个身位
//此时箱子的位置比人会前一个身位,这要求没有箱子在那里,不然就没地放了
for (int k = 1; k < 4; k++) {
if (k == j)
continue; //不会跟自己重叠
if (t.x[j] == t.x[k] && t.y[j] == t.y[k]) {
flag = true; //不合法的操作
break;
}
}
//出界了
if (!isoutside(t.x[j], t.y[j])) {
flag = true; //不合法的操作
break;
}
}
}
if (flag)
continue; //此时走的方向是无解的,换一个方向
//合法的话,步数加1
t.step++;
flag = true; //记录是否所有箱子都进洞了,既箱子的位置是不是洞
for (int j = 1; j < 4; j++) {
if (mp[t.x[j]][t.y[j]] != '@') {
flag = false;
break;
}
}
//如果都进洞了,返回结果
if (flag)
return t.step;
//判重
if (isrepeat(t))
continue;
recordrepeat(t);
q[++tt] = t;
}
}
return -1;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%s", mp[i] + 1);
int cnt = 1; //到了第几个箱子
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
//人的位置记录
if (mp[i][j] == 'X') {
q[0].x[0] = i;
q[0].y[0] = j;
}
//箱子的位置记录
if (mp[i][j] == '*') {
q[0].x[cnt] = i;
q[0].y[cnt] = j;
++cnt;
}
}
}
q[0].step = 0; //初始化步数
printf("%d", bfs());
return 0;
}