# 第 261 场周赛题解
# Q1 2027. 转换字符串的最少操作次数 (opens new window)
贪心着改,遇到一个就相当于把这个和后两个都一起改了。
class Solution5890 {
fun minimumMoves(s: String): Int {
val arr = s.toCharArray()
var ans = 0
for (i in arr.indices) {
if (arr[i] != 'O') {
arr[i] = 'O'
if (i + 1 in arr.indices)
arr[i + 1] = 'O'
if (i + 2 in arr.indices)
arr[i + 2] = 'O'
ans++
}
}
return ans
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Q2 2028. 找出缺失的观测数据 (opens new window)
一层一层放,直到放没即可。
class Solution5891 {
fun missingRolls(rolls: IntArray, mean: Int, n: Int): IntArray {
val left = (rolls.size + n) * mean - rolls.sum()
if (left.toDouble() / n > 6.0 || left.toDouble() / n < 1.0) {
return intArrayOf()
}
val ans = IntArray(n)
for (i in 0 until left) {
ans[i % n]++
}
return ans
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Q3 2029. 石子游戏 IX (opens new window)
恶心的石子游戏又来了!
解法一:正向考虑,Alice如何必胜 or 必败。
- 首先都是3的倍数,Alice无可选,直接输
- 0是偶数,则对方选0我方也选0,约掉不用考虑。我方选1,对方只能选1(选0我们也选0)。之后对方选几我们就选互补值,最后对方没得选。
- 0是奇数,我方选1,对方可以选0,然后我方选1 or 对方选1我方选0,这时消耗两个1,重新转回上述Case2的状态。
这种方法有点乱... 代码出错不易发现,可以使用下面方法二来无视逻辑,只跟规则走。
class Solution5892 {
fun stoneGameIX(stones: IntArray): Boolean {
val arr = IntArray(3)
for (x in stones)
arr[x % 3] += 1
// Alice 先手,无石子可选
if (arr[1] == 0 && arr[2] == 0) return false
if (arr[0] % 2 == 0) {
// Alice 先手,选1必胜
if (arr[1] != 0 && arr[1] - 1 < arr[2]) return true
// Alice 先手,选2必胜
if (arr[2] != 0 && arr[2] - 1 < arr[1]) return true
} else {
// Alice 先手,选1必胜(过渡回上述状态)
if (arr[1] != 0 && arr[2] < arr[1] - 2) return true
if (arr[2] != 0 && arr[1] < arr[2] - 2) return true
}
return false
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
解法二:DFS硬搜
按照规则来处理,简化Alice与Bob的判断,在所有的可能性中进行搜索。
三个数字可以进行简化,0直接%2即可。对于1与2,需要让他们减少至最小值>=1。若本身存在0,则无需简化。
这种判断不用太过思考内部逻辑,只需要处理好数据剪枝即可。
class Solution5892 {
fun stoneGameIX(stones: IntArray): Boolean {
val arr = IntArray(3)
stones.forEach {
arr[it % 3]++
}
if (arr[1] == 0 && arr[2] == 0) return false
if (arr[1] != 0 || arr[2] != 0) arr[0] %= 2
val div = 2
val min = minOf(arr[1] / div, arr[2] / div)
if (min != 0) {
arr[1] -= (min - 1) * div
arr[2] -= (min - 1) * div
}
val seen = HashMap<String, Boolean>()
fun dfs(cur: Int, left: IntArray, alice: Boolean): Boolean {
val key = "$cur, ${left.joinToString()} $alice"
if (key in seen) return seen[key]!!
if (cur > 0 && cur % 3 == 0) {
return alice
}
if (left.sum() == 0) {
return false
}
if (!alice) {
// Bob走让Alice输的步骤
// ans 使用 and
var ans = true
for (i in 0 until 3) {
if (left[i] == 0) continue
val next = left.clone() as IntArray
next[i]--
ans = ans and dfs(cur + i, next, !alice)
}
return ans
}
// Alice走能让自己赢的步骤
// ans 使用 or
var ans = false
for (i in 0 until 3) {
if (left[i] == 0) continue
val next = left.clone() as IntArray
next[i]--
ans = ans or dfs((cur + i) % 3 + 3, next, !alice)
}
return ans.also {
seen[key] = it
}
}
return dfs(0, arr, true)
}
}
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
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
# Q4 2030. 含特定字母的最小子序列 (opens new window)
使用单调栈处理。需要计算当前栈内含有letter的字符数,以及剩余内容还有letter的总数。若总数足够,弹栈的时候才可以弹letter,否则不能弹letter。其次,要保证当前栈空余部分都塞入letter可以满足repetition。
该题目比Q3要简单,只是Q3花费时间太多,导致这题思考时间不够了。
// 单调栈
class Solution5893 {
fun smallestSubsequence(s: String, k: Int, letter: Char, repetition: Int): String {
val st = Stack<Char>()
// 剩余letter
var lst = s.count { it == letter }
// 当前持有letter
var cur = 0
for (i in s.indices) {
// 1. 满足总长度k
// 2. 满足字符重复repetition
while (st.isNotEmpty() && s[i] < st.peek() && st.size + s.length - i > k) {
if (st.peek() == letter) {
if (cur + lst > repetition) {
cur--
st.pop()
} else {
break
}
} else {
st.pop()
}
}
st.push(s[i])
if (s[i] == letter) {
cur++
lst--
}
if (st.size > k) {
if (st.peek() == letter) cur--
st.pop()
}
// 腾出足够的位置给letter
while (st.isNotEmpty() && k - st.size < repetition - cur) {
if (st.peek() == letter) cur--
st.pop()
}
}
return st.joinToString("")
}
}
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
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