Educational Codeforces Round 157 Rated for Div. 2
A. Treasure Chest
题目链接
题目大意
Monocarp 知道宝箱和钥匙的位置都在X轴的正半轴上,Monocarp 在原点,宝箱在
x
x
x 点,宝箱的钥匙在
y
y
y 点上,并且 Monocarp 能够搬运
k
k
k 秒箱子(整个过程只能搬
k
k
k 秒,
1
≤
x
,
y
≤
100
;
x
≠
y
;
0
≤
k
≤
100
1≤x,y≤100; x≠y; 0≤k≤100
1≤x,y≤100;x=y;0≤k≤100)。
Monocarp 能进行以下操作:
- 向左或向右 1 1 1 个单位长度(需要 1 1 1 秒)
- 当前位置拿上钥匙,拿起、放下或打开箱子(需要 0 0 0 秒)
请问 Monocarp 最少需要多少秒打开箱子?
解题思路
只有 3 3 3 种情况:
- 钥匙在去宝箱的路上,那么就是到宝箱的位置的时间
- 钥匙在宝箱的后面,可以把宝箱拿到钥匙处 ( x + k ≥ y ) (x+k≥y) (x+k≥y),那么就是到钥匙位置的时间
- 钥匙在宝箱的后面,可以把宝箱拿到靠近钥匙处 ( x + k < y ) (x+k<y) (x+k<y),那么就是到钥匙的位置所需时间+折返回到宝箱的位置所需时间
AC代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t; cin>>t;
while(t--)
{
int x, y, k;
cin>>x>>y>>k;
int ans = x; // 钥匙在去宝箱的路上
if(y > x) // 钥匙在宝箱后面
{
ans = y; // 默认可以将宝箱带到钥匙处
int d = y-x-k;
if(d > 0) ans += d; // 需要折返到宝箱处
}
cout<<ans<<'\n';
}
return 0;
}
B. Points and Minimum Distance
题目链接
B. Points and Minimum Distance
题目大意
给你
2
n
2n
2n 个整数,你需要将它们组成
n
(
2
≤
n
≤
100
)
n(2≤n≤100)
n(2≤n≤100) 个点的坐标(可以相同),然后选择出一条经过这些点的路径(每点至少经过一次),使得该路径的长度最短。
两点间的距离为:
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
|x_1−x_2|+|y_1−y_2|
∣x1−x2∣+∣y1−y2∣
如果有多个答案,打印其中任何一个。
解题思路
将 2 n 2n 2n 个整数排序,前 n n n 个作为 x x x 坐标,后 n n n 个作为 y y y 坐标。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 210;
int a[N];
int main()
{
int t; cin>>t;
while(t--)
{
int n; cin>>n;
for(int i=0; i<2*n; i++) cin>>a[i];
sort(a, a+2*n);
int ans = 0;
for(int i=1; i<n; i++)
ans += (a[i]-a[i-1]) + (a[n+i]-a[n+i-1]);
cout<<ans<<'\n';
for(int i=0; i<n; i++)
printf("%d %d\n", a[i], a[n+i]);
}
return 0;
}
C. Torn Lucky Ticket
题目链接
题目大意
票证都是由
1
−
9
1-9
1−9 对应的字符构成的字符串。
幸运的票证的定义是:
- 票证的长度为偶数
- 前半部分的数字之和等于后半部分的数字之和(对半分)
现在,你有
n
n
n 个残缺的票证
s
1
,
s
2
,
…
,
s
n
s_1,s_2, …,s_n
s1,s2,…,sn,请你找一找下标
(
i
,
j
)
(i,j)
(i,j) 有多少对组成的票证是幸运票证。
(
1
≤
i
,
j
≤
n
)
(1≤i,j≤n)
(1≤i,j≤n)
特别注意: 当
i
≠
j
i≠j
i=j 时,
(
i
,
j
)
(i,j)
(i,j) 和
(
j
,
i
)
(j,i)
(j,i) 视为不同的一对,当
i
=
j
i=j
i=j 时,是同一对。
解题思路
在输入
n
n
n 个票证时,记录每个票证的第
i
i
i 位的前缀和,同时利用二维数组记录在相同的票证长度下的所有数字之和相同的个数。
遍历每个残缺的票证,然后枚举拼接后的长度得到需要找到另外一个残缺票证的长度,进而找到能与其拼接成幸运票证的残缺票证个数相加得到答案。拼接成幸运的票证大致可以分为
3
3
3 种情况:
- 直接加上与该残缺票证相同长度且所有数字之和相等的个数(包括了自己和自己拼接)
- 左拼接:加上长度为 拼接后的长度-该残缺票证长度且数字之和为该残缺票证的数字之和-2*该残缺票证前(该残缺票证长度-拼接后长度/2)个数字之和的个数。
- 右拼接:加上长度为 拼接后的长度-该残缺票证长度且数字之和为2*该残缺票证的前(拼接后长度/2)个数字之和-该残缺票证的数字之和的个数。
特别注意: 显然枚举得到拼接后的长度要比遍历的残缺票证大,但它的一半得比残缺票证小,这样才好找到符合要求的个数。
不然会重复甚至不好找,就比如拿短的去找比它长的另一半,长的限制条件表面虽然好确定,但是实际上不好找到(长度好确定,但是长的通用数字和不好确定),又得一个个遍历判断一遍。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int strS[N][7]; // n 个残缺票证及其前缀和
int cnt[6][N]; // 相同长度且相同数字之和的个数
int main()
{
int n;
scanf("%d", &n);
for(int i=0; i<n; i++)
{
char str[10];
scanf(" %s", str);
int len = (int)strlen(str);
for(int j=0; j<len; j++)
strS[i][j+1] = strS[i][j] + (str[j]-'0');
strS[i][0] = len;
int sum = strS[i][len];
cnt[len][sum]++;
}
long long ans = 0;
for(int i=0; i<n; i++)
{
int len = strS[i][0];
int sum = strS[i][len];
// 相同长度且相同数字之和拼接
ans += cnt[len][sum];
for(int j=1; j<len; j++)
{
if(2*j <= len) continue; // 枚举长度
// 左拼接
int l = sum - 2*strS[i][len-j];
if(l > 0) ans += cnt[2*j-len][l];
//右拼接 sum - 2*(sum-strS[i][j]) -> 2*strS[i][j] - sum
int r = 2*strS[i][j] - sum;
if(r > 0) ans += cnt[2*j-len][r];
}
}
printf("%lld", ans);
return 0;
}