Kick Start Round H 2020解题记录

Retype

题目描述

题目链接:https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff49/000000000043adc7#problem

解题思路

这题比较简单,想要在当前关执行任何操作(进入下一层、返回上一层、退出、重开游戏)都需要花费1min的时间。

因此仅需要直接计算,重开和返回到k层取剑两种情况所需花费的总时间即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <cstdio>

using namespace std;

int cal(int n, int k, int s) {
int go_back = 2 * (k - s) + n;
int restart = n + k;

return go_back < restart? go_back : restart;
}

int main() {
int T, N, S, K;
scanf("%d", &T);


for (int i = 0; i < T; ++i) {
scanf("%d%d%d", &N, &K, &S);
printf("Case #%d: %d\n", i + 1, cal(N, K, S));
}

return 0;
}

Boring Numbers

题目描述

题目链接:https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff49/000000000043b0c6

解题思路

很容易发现,奇偶各有5个,也就是说对于n位数而言,区间$[1, 10 ^ {\left (n + 1 \right)} )$内满足boring number的数字有$5 ^ n$个。

然后对于前缀,如果前缀中存在不满足的数字,那么后续也就没必要继续计算了,如1334,仅需考虑$[1, 1299]$或者说$[1, 1300]$的情况。

最后对于Test Set 2的数字,没有必要进行暴力计算,利用求$[1, L - 1]$和$[1, R]$两个区间的数量来计算$[L, R]$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <cstdio>
#include <vector>
#include <string>

using namespace std;

class Solution {
public:
Solution() {
// even - 0 2 4 6 8
// odd - 1 3 5 7 9
// 1 2 3 4 5
memo.resize(20, 1);
pre.resize(20, 0);

for (int i = 1; i < 20; ++i) {
memo[i] = memo[i - 1] * 5;
}

for (int i = 1; i < 20; ++i) {
pre[i] = pre[i - 1] + memo[i];
}
}

long long solve(long long l, long long r) {
return cal(r) - cal(l - 1);
}

private:
vector<long long> memo; // 存储i位能得到的boring numbers的数量
vector<long long> pre; // 存储[1, 9], [1, 99], [1, 999]...区间内的boring numbers的数量
// 0 1 2 3 4 5 6 7 8 9
vector<int> a = {0, 0, 1, 1, 2 ,2 ,3 ,3 ,4, 4}; // 某高位与下标满足boring number时,某高位可选数字的数量
// 最高位的位置一定是1,因此如果某次高位的值是偶数,且满足boring number,
// 那么意味着它可以选择0,也即0不会出现在最高位

vector<int> b = {0, 1, 1, 2, 2, 3, 3, 4, 4, 5}; // 某高位与下标不满足boring number时,某高位可选数字的数量


long long cal(long long n) {
string s = to_string(n);
int size = s.size();
long long res = pre[size - 1];

for (int i = 1; i <= size; ++i) {
int num = s[i - 1] - '0';

if (num % 2 == i % 2) {
res += memo[size - i] * a[num];
if (i == size) {
// 参照a数组可以发现,其计算并未考虑自身的值,
// 因此对于最低位来说,缺少了对自身满足的计算,因此补上1
++res;
}
} else {
res += memo[size - i] * b[num];
break;
}

}
return res;
}
};

int main() {
int T;
scanf("%d", &T);

long long L, R;
Solution A;

for (int i = 1; i <= T; ++i) {
scanf("%lld%lld", &L, &R);
printf("Case #%d: %lld\n", i, A.solve(L, R));
}

return 0;
}

Rugby

题目描述

题目链接:https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff49/000000000043b027#problem

解题思路

首先按照移动规则,很容易得知,水平移动和垂直移动是分开计算的,因此不需要将X与Y放在一起存储。

然后先观察Y,不难论证,水平线的位置应该在中位数上。但是对于X由于需要满足相邻的点距离1,不能直接采取Y取中位数的做法。因此需要先进行$X_i - K_i$的处理,$K_i$代表$X_i$左侧的player的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cinttypes>

using namespace std;

int64_t SolveX (vector<int>& x_list, int N) {
int64_t total = 0;

sort(x_list.begin(), x_list.end());

for (int i = 0; i < N; ++i) {
// 已知了所有点的相对位置,可以求出相较于自己最终位置的相对坐标
// 这样就将X转换为了Y的处理方式,找到中位数,将所有的修改后的X归于同一点
x_list[i] -= i;
}

// 取出这些差值的中位数
sort(x_list.begin(), x_list.end());

for (int i = 0; i < N; ++i) {
total += abs(x_list[i] - x_list[N / 2]);
}

return total;
}

int64_t SolveY (vector<int>& y_list, int N) {
int64_t total = 0;

sort(y_list.begin(), y_list.end());

for (int i = 0; i < N; ++i) {
total += abs(y_list[i] - y_list[N / 2]);
}

return total;
}

int main() {
int T, N, X, Y;
scanf("%d", &T);

for (int i = 1; i <= T; ++i) {
scanf("%d", &N);

// 因为x与y是分开计算的,因此不需要一起处理
vector<int> x_list(N);
vector<int> y_list(N);

for (int j = 0; j < N; ++j) {
scanf("%d%d", &X, &Y);
x_list[j] = X;
y_list[j] = Y;
}

printf("Case #%d: %" PRId64"\n", i, SolveX(x_list, N) + SolveY(y_list, N));
}

return 0;
}

Friend

题目描述

题目链接:https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff49/000000000043aee7

解题思路

这道题的主要难点在于构建图。如果使用名字作为点,那么边的数量会非常大。因此可以考虑使用字母作为点。如果一个人的名字为LIZZIE,那么其与名字中带“L、I、Z、E“的均存在连线。因此用字母替代的方式可行。这里图矩阵的含义就代表着graph[i][j] = 1,名字带i的与名字带j的之间存在1个名字使得他们联通。

然后在建好图之后,很明显是一个求任意两点间的最短距离的问题,那么就可以使用Floyd算法求解。

这道题有一个点需要注意:用在Floyd中标记不可达的无穷大,不能选择INT_MAX。因为该值在计算的时候会产生溢出,就会导致在更新最短距离时,INT_MAX溢出为负的值会成为最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <cstdio>
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

#define INF 0x3f3f3f3f

using namespace std;

class Solution {
public:
Solution(int N) {
graph.resize(26, vector<int>(26, INF));
names.resize(N + 1);
string s;

for (int i = 1; i <= N; ++i) {
cin >> s;
names[i] = s;

for (int j = 0; j < s.size(); ++j) {
for (int k = j + 1; k < s.size(); ++k) {
if (s[j] != s[k]) {
graph[s[j] - 'A'][s[k] - 'A'] = 1;
graph[s[k] - 'A'][s[j] - 'A'] = 1;
}
}
}
}

floyd();
}


int solve(int x, int y) {
int min_dist = INF;

for (char cx : names[x]) {
for (char cy : names[y]) {
if (cx == cy) {
return 2;
}

min_dist = min(min_dist, graph[cx - 'A'][cy - 'A'] + 2);
}
}

return min_dist == INF? -1 : min_dist;
}


private:
int n;

vector<vector<int>> graph;
vector<string> names;

void floyd() {
for (int k = 0; k < 26; k++) {
for (int i = 0; i < 26; i++) {
for (int j = 0; j < 26; j++) {
graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);
}
}
}
}

};

int main() {
int T, N, Q, P1, P2;
scanf("%d", &T);

for (int i = 1; i <= T; ++i) {
scanf("%d%d", &N, &Q);
getchar(); // 读取换行符
Solution A(N);
printf("Case #%d:", i);
for (int j = 0; j < Q; ++j) {
scanf("%d%d", &P1, &P2);
printf(" %d", A.solve(P1, P2));
}
printf("\n");

}

return 0;
}
Author

Chaos Chen

Posted on

2021-03-11

Updated on

2023-06-30

Licensed under

Commentaires