比赛链接:https://atcoder.jp/contests/abc308
比赛时间:2023年7月1日 20:00-21:40
A题:New Scheme
标签:模拟
题意:给定
8
8
8个数的序列,询问这些数是否满足以下条件:
- 在 100 100 100到 675 675 675之间且能被 25 25 25整除
- 序列是单调非递减的
题解:按题意模拟判断就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
int s[10];
int main() {
for (int i = 1; i <= 8; i++) {
cin >> s[i];
if (s[i] < 100 || s[i] > 675 || s[i] % 25 != 0) {
cout << "No";
return 0;
}
}
for (int i = 2; i <= 8; i++) {
if (s[i] < s[i - 1]) {
cout << "No";
return 0;
}
}
cout << "Yes";
return 0;
}
B题:Default Price
标签:
S
T
L
、
m
a
p
STL、map
STL、map、模拟
题意:给定
n
n
n个有颜色的盘子和
m
m
m种颜色分别对应的价值
p
i
p_i
pi,
p
0
p_0
p0表示盘子颜色如果不属于这
m
m
m种颜色,那对应的价值,求这
n
n
n个盘子最后的价值和为多少。
题解:可以先用
m
a
p
map
map去映射一下不同颜色对应的价值,然后去遍历这
n
n
n个盘子,如果映射发现对应的是
0
0
0价值,说明不属于这
m
m
m种颜色,那我们用
p
0
p_0
p0去代替一下,最后累加下就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
map<string, int > a;
string s[105], s2[105];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
for (int i = 1; i <= m; i++) {
cin >> s2[i];
}
int p, v;
cin >> p;
for (int i = 1; i <= m; i++) {
cin >> v;
a[s2[i]] = v;
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (!a[s[i]]) {
ans += p;
} else {
ans += a[s[i]];
}
}
cout << ans << endl;
return 0;
}
C题:Standings
标签:结构体排序、精度
题意:给定
n
n
n人扔硬币,第
i
i
i个人扔硬币,有正面朝上
A
i
A_i
Ai次和反面朝上
B
i
B_i
Bi次。定义成功率为
A
i
A
i
+
B
i
A_i\over A_i+B_i
Ai+BiAi。求按成功率从高到低,成功率相同根据编号从小到大排序。
题解:先把每个人的编号
i
d
id
id按输入顺序设置好,然后根据题目中成功率和编号的要求排序就好了。这道题有个坑点就是,直接用
d
o
u
b
l
e
double
double除出来会有精度缺失,所以我们把除法运算转成乘法运算。如果有两个人
x
x
x和
y
y
y,那我们把不等式的分母乘到对面去得到
(
x
.
a
+
x
.
b
)
∗
y
.
a
<
(
y
.
a
+
y
.
b
)
∗
x
.
a
(x.a + x.b) * y.a < (y.a + y.b) * x.a
(x.a+x.b)∗y.a<(y.a+y.b)∗x.a。
代码:
#include <bits/stdc++.h>
using namespace std;
struct node {
int id;
long long a, b;
}p[2000005];
bool cmp(node x, node y) {
if ((x.a + x.b) * y.a == (y.a + y.b) * x.a) return x.id < y.id;
else return (x.a + x.b) * y.a < (y.a + y.b) * x.a;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i].a >> p[i].b;
p[i].id = i;
}
sort(p + 1, p + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
cout << p[i].id << " ";
}
return 0;
}
D题:Snuke Maze
标签:深度优先搜索
题意:给定一个二维的字符矩阵地图,询问能否从左上角
(
1
,
1
)
(1,1)
(1,1)位置走到右下角
(
n
,
m
)
(n,m)
(n,m)位置,要求按照字符串(snuke): s → n → u → k → e → s → n →…这样的顺序走。
题解:经典的
d
f
s
dfs
dfs问题,按照题目要求从起点深搜下去就好了。深搜问题主要从当前状态往下一个状态走的时候需要考虑几个问题:
- 题目限制(该题就是 snuke 的顺序)。
- 地图边界。
- 该点是否已经走过(避免套娃)。
代码:
#include <bits/stdc++.h>
using namespace std;
string s[505];
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
int n, m, f = 0;
bool vis[505][505];
map<char, int> g;
void dfs(int x, int y, int id) {
if (f || (x == n - 1 && y == m - 1)) {
f = 1;
return ;
}
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (vis[nx][ny]) continue;
if ((g[s[nx][ny]] == id + 1) || (id == 5 && g[s[nx][ny]] == 1)) {
vis[nx][ny] = 1;
dfs(nx, ny, g[s[nx][ny]]);
}
}
}
int main() {
g['s'] = 1; g['n'] = 2; g['u'] = 3;
g['k'] = 4; g['e'] = 5;
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> s[i];
vis[0][0] = 1;
dfs(0, 0, g[s[0][0]]);
if (f) cout << "Yes";
else cout << "No";
return 0;
}
E题:MEX
标签:动态规划、前缀维护、MEX
题意:给定一个长度为
n
n
n的序列
A
1
,
A
2
,
A
3
,
.
.
.
A
n
A_1,A_2,A_3,...A_n
A1,A2,A3,...An,
A
i
A_i
Ai的范围在
0
、
1
、
2
0、1、2
0、1、2。再给定相同长度的字符串
B
B
B(只含
M
、
E
、
X
M、E、X
M、E、X三种字符)。给定规则
M
E
X
MEX
MEX:指除去本身以外其他非负整数的集合中的最小值。目前要求
(
B
i
,
B
j
,
B
k
)
(B_i,B_j,B_k)
(Bi,Bj,Bk)刚好是
M
、
E
、
X
M、E、X
M、E、X,求所有对应的
M
E
X
(
A
i
,
A
j
,
A
k
)
MEX(A_i,A_j,A_k)
MEX(Ai,Aj,Ak)之和为多少。
题解:维护一个前缀
M
M
M出现
0
、
1
、
2
0、1、2
0、1、2的数量,维护一个后缀
X
X
X出现
0
、
1
、
2
0、1、2
0、1、2的数量,然后去枚举
E
E
E,到每一个
E
E
E的时候,看看对应当前的
A
i
A_i
Ai为多少,然后考虑在这之前对应的
M
M
M是
0
、
1
、
2
0、1、2
0、1、2的数量,和在这之后
X
X
X是
0
、
1
、
2
0、1、2
0、1、2的数量。然后枚举这
9
9
9种情况,每种情况贡献的值为:前缀
M
M
M的数量
∗
*
∗后缀
X
X
X的数量
∗
*
∗
M
E
X
(
a
[
i
]
,
j
,
k
)
MEX(a[i], j, k)
MEX(a[i],j,k)。
j
,
k
j,k
j,k为枚举
0
、
1
、
2
0、1、2
0、1、2的情况。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 10;
ll n, a[N], m[N][10], x[N][10], ans = 0;
char b[N];
int cal(int x, int y, int z) {
for (int i = 0; i <= 3; i++) {
if (x != i && y != i && z != i) return i;
}
return 0;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
if (b[i] == 'M') {
m[i][a[i]]++;
}
m[i][0] += m[i-1][0];
m[i][1] += m[i-1][1];
m[i][2] += m[i-1][2];
}
for (int i = n; i >= 1; i--) {
if (b[i] == 'X') {
x[i][a[i]]++;
}
x[i][0] += x[i+1][0];
x[i][1] += x[i+1][1];
x[i][2] += x[i+1][2];
}
for (int i = 1; i <= n; i++) {
if (b[i] == 'E') {
if (a[i] == 0) {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
ans += m[i][j] * x[i][k] * cal(0, j, k);
}
}
}
else if (a[i] == 1) {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
ans += m[i][j] * x[i][k] * cal(1, j, k);
}
}
} else {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
ans += m[i][j] * x[i][k] * cal(2, j, k);
}
}
}
}
}
cout << ans << endl;
return 0;
}
F题:Vouchers
标签:贪心、堆优化
题意:给定
n
n
n个商品,每个商品价格为
p
i
p_i
pi元,有
m
m
m张优惠券,第
i
i
i优惠券至少要购买价格为
L
i
L_i
Li元的商品才能折扣
D
i
D_i
Di元,每张优惠券只能用一次,同一物品不能使用多张优惠券。求购买所有商品的最少总金额为多少。
题解:按照出售价格从小往大,因为有一个条件
L
>
D
L>D
L>D,所以不可能出现折扣大于商品的情况,用一个堆保存小于该商品售价所有
L
L
L对应的折扣
D
D
D,每次贪心取折扣最大的,取完就弹出堆,保证只取一次。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, p[200005], ans;
priority_queue<int> q;
pair<int, int> c[200005];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> p[i];
ans += p[i];
}
for (int i = 1; i <= m; i++) cin >> c[i].first;
for (int i = 1; i <= m; i++) cin >> c[i].second;
sort(p + 1, p + 1 + n);
sort(c + 1, c + 1 + m);
for (int i = 1, pos = 1; i <= n; i++) {
while (pos <= m && c[pos].first <= p[i]) {
q.push(c[pos].second);
pos++;
}
if (!q.empty()) {
ans -= q.top(); q.pop();
}
}
cout << ans << endl;
return 0;
}