Сенат Dota2
Описание задачи #
В мире Dota2 существует 2 партии Radiant
и Dire
.
Для того чтобы внести изменение в игру Dota2 необходимо провести голосование сената, каждый член которого принадлежит одной из двух партий.
Голосование идет раундами, в каждом раунде по очереди опрашивается каждый сенатор. Сенатор может выполнить одно из двух действий:
- лишить следующего сенатора из противоположной партии права голоса;
- объявить победу своей партии, если не осталось сенаторов с правом голоса из противоположной партии. Раунд начинается с крайнего левого сенатора.
Напишите функцию, которая будет рассчитывать результаты голосования сената.
Входные данные
Строка, описывающая последовательность голосующих сенаторов и их принадлежность к партиям.
Выходные данные
Строка с названием победившей партии (Radiant
или Dire
).
Ограничения #
- Количество сенаторов (длина входящей строки) находится в диапазоне от 1 до 10000
- Входящая строка состоит из символов
R
иD
Примеры #
-
Входные данные:
RD
Ответ:
Radiant
Пояснение: В первом раунде первый сенатор из партии
Radiant
лишает права голоса сенатора изDire
. На этом раунд заканчивается, так как не осталось сенаторов с правом голоса. Во втором раунде сенатор из партииRadiant
объявляет победу своей партии. -
Входные данные:
RDD
Ответ:
Dire
Пояснение: В первом раунде сентаор из
Radiant
лишает одного из сенаторовDire
права голоса. Второй сенатор изDire
лишает права голоса сенатора изRadiant
и в следующем раунде объявляет победу своей партии.
Решение #
На первый взгляд задача может решиться простым сравнением количества сенаторов из каждой партии. На самом деле такое решение будет не верно, так как не учитывает порядок голосования.
Давайте рассмотрим на простом примере: RDRDRDRDRDDD
.
В данном примере 5 сенаторов в партии Radiant
и 7 в Dire
.
Давайте разберем по шагам данный пример (символом _
будем отмечать сенаторов лишенных права голоса).
- Очевидно, что для выигрышной стратегии каждый сенатор из
Radiant
будет лишать голоса следующего за ним сенатора изDire
. Таким образом, учитывая очередность голосования, в первом раунде 5 первых подряд проголосовавших сенатора будут изRadiant
. После их голосования картина будет выглядеть следующим образом:R_R_R_R_R_DD
. - Таким образом, голосовать в первом раунде смогут только 2 сенатора из
Dire
. Финальный исход первого раунда будет следующим:____R_R_R_DD
- Во втором раунде голосовать снова начнут сенаторы из
Radiant
. Первые два сенатора лишат голосов двух оставшихся сенаторов изDire
, третий сможет объявить победу своей партии. Финальный исход первого раунда будет следующим:____R_R_R___
.
Таким образом, несмотря на количественное преимущество партии Dire
, победу одержала Radiant
.
На самом деле, в решении данной задачи нам поможет одна из базовых структур данных - очередь. В качестве очереди мы будем использовать массив. В данном случае можно забыть про то, что голосование проходит по раундам и представить весь процесс как непрерывный цикл.
Изначально мы помещаем всех сенаторов в очередь в соответствии с их порядком и подсчитываем общее количество представителей каждой из партий.
Далее, пока в очереди присутствуют представители обеих партий мы запускаем наш непрерывный цикл.
- Сохраняем принадлежность сенатора, стоящего во главе очереди в переменную
firstElem
. - Вводим дополнительный счетчик
killCount
. В этом счетчике мы будем накапливать кол-во сенаторов, которых смогут лишить права голоса представители партииfirstElem
до момента, пока права голоса не перейдет к представителям противоположной партии. - Начинаем извлекать из очереди сенаторов:
- Если сенатор принадлежит к той же партии, что и
firstElem
, тогда увеличиваем счетчикkillCount
и помещаем сенатор в конец очереди. - Если сенатор принадлежит к противоположной партии - уменьшаем счетчик
killCount
и пропускаем (НЕ возвращаем в очередь) сенатора. - Если
killCount
становится равным 0, значит сенаторы из партииfirstElem
не могут лишить права голоса следующего сенатора - вfirstElem
помещается партия следующего на очереди сенатора и подсчет начинается с начала до тех пор, пока в очереди есть представители обеих партий.
- Если сенатор принадлежит к той же партии, что и
-
package main func predictPartyVictory(senate string) string { rCount, dCount := 0, 0 queue := make([]byte, 0, 2*len(senate)) for i := 0; i < len(senate); i++ { if senate[i] == 'R' { rCount++ } else { dCount++ } queue = append(queue, senate[i]) } for rCount > 0 && dCount > 0 { firstElem := queue[0] killCount := 1 queue = append(queue, firstElem) queue = queue[1:] for killCount > 0 && rCount > 0 && dCount > 0 { if queue[0] == firstElem { queue = append(queue, queue[0]) queue = queue[1:] killCount++ } else { if queue[0] == 'R' { rCount-- } else { dCount-- } killCount-- queue = queue[1:] } } } if rCount > 0 { return "Radiant" } return "Dire" }
-
const moveFirst = (arr: string[]) => { const first = arr.shift() if (first) { arr.push(first) } } export const predictPartyVictory = (senate: string): string => { let rCount = 0 let dCount = 0 const queue: string[] = [] for (let i = 0; i < senate.length; i++) { if (senate[i] === 'R') { rCount++ } else { dCount++ } queue.push(senate[i]) } while (rCount > 0 && dCount > 0) { let firstElem = queue[0] let killCount = 1 moveFirst(queue) while (killCount > 0 && rCount > 0 && dCount > 0) { if (queue[0] == firstElem) { moveFirst(queue) killCount++ } else { if (queue[0] == 'R') { rCount-- } else { dCount-- } killCount-- queue.shift() } } } if (rCount > 0) { return "Radiant" } return "Dire" }
Оценка сложности #
По времени
Сложность O(n)
, где n
— длина строки.
По памяти
Сложность O(n)
, так как нам пришлось создать очередь для хранения данных.