649. Dota2 Senate
MediumDescription
Dota2 世界中有兩個陣營:Radiant (R) 和 Dire (D)。參議員們輪流行使權力,每人可以禁止另一陣營的一位參議員。投票輪次持續進行直到某個陣營的所有參議員都被禁止。假設每位參議員都採最優策略,回傳獲勝陣營。
Example:
Input: senate = “RDD” Output: “Dire”
Intuition#
貪心策略:每個參議員應該禁止下一個即將行動的對方陣營參議員。
- 使用兩個佇列分別存放 R 和 D 的索引
- 每輪比較兩個佇列的隊首,索引較小的(先行動的)禁止對方
- 勝者加上
n(代表下一輪)重新入隊 - 直到某個佇列為空
Approaches#
1: 模擬#
- 概念: 用布林陣列標記被禁止的參議員,模擬多輪投票過程。
- 時間複雜度:
O(n^2)最壞情況 - 空間複雜度:
O(n)
class Solution {
fun predictPartyVictory(senate: String): String {
val active = BooleanArray(senate.length) { true }
var rBan = 0; var dBan = 0
var rCount = senate.count { it == 'R' }
var dCount = senate.length - rCount
while (rCount > 0 && dCount > 0) {
for (i in senate.indices) {
if (!active[i]) continue
if (senate[i] == 'R') {
if (rBan > 0) { rBan--; active[i] = false; rCount-- }
else dBan++
} else {
if (dBan > 0) { dBan--; active[i] = false; dCount-- }
else rBan++
}
}
}
return if (rCount > 0) "Radiant" else "Dire"
}
}⭐2: 雙佇列貪心#
- 概念: 兩個佇列存放各陣營參議員的索引,每次兩隊首比較,索引小的勝出並重新入隊。
- 時間複雜度:
O(n) - 空間複雜度:
O(n)
class Solution {
fun predictPartyVictory(senate: String): String {
val n = senate.length
val rQueue = ArrayDeque<Int>()
val dQueue = ArrayDeque<Int>()
for (i in senate.indices) {
if (senate[i] == 'R') rQueue.add(i) else dQueue.add(i)
}
while (rQueue.isNotEmpty() && dQueue.isNotEmpty()) {
val r = rQueue.removeFirst()
val d = dQueue.removeFirst()
if (r < d) rQueue.add(r + n) else dQueue.add(d + n)
}
return if (rQueue.isNotEmpty()) "Radiant" else "Dire"
}
}🔑 Takeaways#
- Pattern: 貪心 + 佇列模擬 — 先手優勢,禁止最近的對手
- 關鍵技巧: 勝者加
n重新入隊模擬循環投票;雙佇列比模擬高效得多