The Dota2 Senate problem presents a simulation scenario involving two parties: Radiant (R
) and Dire (D
). The senate is represented as a string senate
, where each character is either 'R'
or 'D'
, indicating the party of the senator at that position.
The process follows these rules:
"Radiant"
or "Dire"
.At first glance, this problem appears to require simulating the banning process as described. A brute-force approach would be to keep track of active senators and, for each, ban an opponent in each round. However, this could become inefficient as the number of senators increases, due to repeated scanning and removal.
To optimize, we notice that the banning process is essentially a queueing system: senators act in order, and after acting, they rejoin the end of the queue if they are not banned. If we can efficiently track which senator is next from each party, and always ensure the earliest one acts first, the simulation becomes much more efficient.
The key realization is that we don't need to keep the entire senate string, but rather the indices of the remaining senators from each party. We can use two queues—one for Radiant, one for Dire—to represent their turns. Each time a senator acts, they "ban" the first senator from the other party whose turn comes after theirs, and then rejoin the end of the queue (with their index incremented by the length of the senate, to simulate wrapping around).
The optimized approach uses two queues to track the positions of active senators from each party:
R
) and one for Dire (D
), storing the indices of the senators.
This method ensures each senator acts in order, and efficiently simulates the banning process without repeatedly scanning the senate string.
Let's consider the input senate = "RDD"
:
Brute-force approach: For each round, you may need to scan the entire senate list to find the next active senator, leading to O(n2) time in the worst case.
Optimized queue-based approach:
The Dota2 Senate problem is elegantly solved by modeling the process with two queues, tracking the order and position of each party's senators. This avoids unnecessary repeated scans and simulates the banning process efficiently. By always comparing the earliest available senator from each party, we maintain the correct turn order and ensure a linear-time solution.
from collections import deque
class Solution:
def predictPartyVictory(self, senate: str) -> str:
n = len(senate)
radiant = deque()
dire = deque()
for i, c in enumerate(senate):
if c == 'R':
radiant.append(i)
else:
dire.append(i)
while radiant and dire:
r = radiant.popleft()
d = dire.popleft()
if r < d:
radiant.append(r + n)
else:
dire.append(d + n)
return "Radiant" if radiant else "Dire"
#include <queue>
#include <string>
using namespace std;
class Solution {
public:
string predictPartyVictory(string senate) {
int n = senate.size();
queue<int> radiant, dire;
for (int i = 0; i < n; ++i) {
if (senate[i] == 'R')
radiant.push(i);
else
dire.push(i);
}
while (!radiant.empty() && !dire.empty()) {
int r = radiant.front(); radiant.pop();
int d = dire.front(); dire.pop();
if (r < d)
radiant.push(r + n);
else
dire.push(d + n);
}
return radiant.empty() ? "Dire" : "Radiant";
}
};
import java.util.*;
class Solution {
public String predictPartyVictory(String senate) {
int n = senate.length();
Queue<Integer> radiant = new LinkedList<>();
Queue<Integer> dire = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (senate.charAt(i) == 'R')
radiant.add(i);
else
dire.add(i);
}
while (!radiant.isEmpty() && !dire.isEmpty()) {
int r = radiant.poll();
int d = dire.poll();
if (r < d)
radiant.add(r + n);
else
dire.add(d + n);
}
return radiant.isEmpty() ? "Dire" : "Radiant";
}
}
var predictPartyVictory = function(senate) {
const n = senate.length;
const radiant = [];
const dire = [];
for (let i = 0; i < n; i++) {
if (senate[i] === 'R') radiant.push(i);
else dire.push(i);
}
while (radiant.length && dire.length) {
const r = radiant.shift();
const d = dire.shift();
if (r < d) radiant.push(r + n);
else dire.push(d + n);
}
return radiant.length ? "Radiant" : "Dire";
};