# 第 230 场周赛题解

# Q1 5689. 统计匹配检索规则的物品数量 (opens new window)

正常的模拟即可,对于Kotlin可以使用count函数来简化代码。

class Solution5689 {
    fun countMatches(items: List<List<String>>, ruleKey: String, ruleValue: String): Int {
        return items.count {
            when (ruleKey) {
                "type" -> it[0] == ruleValue
                "color" -> it[1] == ruleValue
                else -> it[2] == ruleValue
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

# Q2 5690. 最接近目标价格的甜点成本 (opens new window)

可以看到,基料和配料的个数n,mn, m的范围是1<=n,m<=101 <= n, m <= 10,因此直接用强搜索即可。

搜索树每个节点有3种选择,时间复杂度为O(n3m)O(n * 3^m)

image-20210301164504311

import kotlin.math.abs

class Solution5690 {
    fun closestCost(baseCosts: IntArray, toppingCosts: IntArray, target: Int): Int {
        var ans = Int.MAX_VALUE / 2
        fun dfs(cur: Int, index: Int) {
            if (abs(cur - target) < abs(ans - target)) {
                ans = cur
            } else if (abs(cur - target) == abs(ans - target) && cur < ans) {
                ans = cur
            }
            if (index !in toppingCosts.indices) return
            dfs(cur, index + 1)
            dfs(cur + toppingCosts[index], index + 1)
            dfs(cur + toppingCosts[index] * 2, index + 1)
        }
        for (i in baseCosts) {
            dfs(i, 0)
        }
        return ans
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Q3 5691 通过最少操作次数使数组的和相等 (opens new window)

直接贪心可做。两个数组的差值为diffdiff,而每个操作可以减小这个差值可以有贪心的逻辑,即对于总和小的数组,可以把当前最小的值变为66,对于总和大的数组可以把当前最大的值变为11

  1. 所有的值变为6,贡献的差值为6it6 - it
  2. 所有的值变为1,贡献的差值为it1it - 1

这样我们可以把每个数字的操作后的价值进行排序,当将差值diffdiff变成负数时,则操作完成。

class Solution5691 {
    fun minOperations(nums1: IntArray, nums2: IntArray): Int {
        val a = nums1.sum()
        val b = nums2.sum()
        if (a == b) return 0
        val arr = arrayListOf<Int>()
        if (a < b) return minOperations(nums2, nums1)
        nums1.forEach { arr.add(it - 1) }
        nums2.forEach { arr.add(6 - it) }
        arr.sortDescending()
        var cur = a - b
        var i = 0
        while (cur > 0) {
            if (i !in arr.indices) return -1
            cur -= arr[i]
            i++
        }
        return i
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Q4 5692. 车队 II (opens new window)

本题可以通过之前小乌龟题目进行理解,首先两辆车相遇时,所谓的降低速度可以完全理解为后车直接消失。

每辆车只能追击到自己右侧的车,我们可以从右到左遍历数组,每次判断是否可以与右侧的车相遇。这种暴力的方法时间复杂度为O(n2)O(n^2),题目中给出的数据范围会超时,需要优化。

对于a,b,c,da, b, c, d来说,共有以下几种可能:

  1. cc无法追上dd,那么a,ba, b无需判断能否追上dd,因为追上cc时则会消失(即判断时使用栈,从左到右来判断)
  2. cc能追上dd,判断bb追击时不能简单的直接判断速度,还需要通过之前得出的cc追击dd的结果时间。
    1. 若在cc追上dd的时间点,bb已经追上cc了,那么就直接使用这个结果
    2. 若此时bb还未追上cc,那么就不用再判断cc,直接判断dd即可(当做c此时已经消失)
    3. bb只能追到dd,那么当aa判断时,也就不需要判断是否能追击到cc了,要么aa追上bb自己消失,要么等aa追上dd。这里可以用反证,若aa追上cc时,bb已经追上dd,那么cc也已经撞到dd并消失了。若aa追上cc时,b未追到dd,则与逻辑相悖,不可能跨过bb先追上cc
  3. 用栈记录当前右侧车队,被后车追上之前已经消失的车可以直接弹栈。

时间复杂度为O(n)O(n),栈的最大深度为nn,且栈中元素判断一次只能有弹栈确定一个结果两个可能。

class Solution5692 {
    fun getCollisionTimes(cars: Array<IntArray>): DoubleArray {
        val n = cars.size
        val ans = DoubleArray(cars.size) { -1.0 }
        val st = Stack<Int>()

        fun calc(i: Int): Double {
            return (cars[st.peek()][0] - cars[i][0]).toDouble() / (cars[i][1] - cars[st.peek()][1])
        }

        for (i in n - 1 downTo 0) {
            while (st.isNotEmpty() &&
                    ans[st.peek()] > 0 &&
                    (cars[i][1] <= cars[st.peek()][1] || calc(i) > ans[st.peek()])) {
                st.pop()
            }
            if (st.isEmpty() || cars[i][1] <= cars[st.peek()][1]) {
                ans[i] = -1.0
            } else {
                ans[i] = calc(i)
            }
            st.push(i)
        }
        return ans
    }
}
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