Вступление
На всеобщих выборах хотелось бы рассчитать постоянную цену за место в парламенте. Это означает, что для N >= 0
распределения мест и списка ns
голосов для каждой партии мы бы хотели найти число d
,
sum(floor(n/d) for n in ns) == N
Чтобы сделать вещи интересными (и больше похожими на реальный мир), мы добавим еще два факта:
Две партии могут собираться в «коалицию», так что места в «коалиции» предоставляются суммой голосов за все партии в ней. Затем места, которые получила «коалиция», распределяются между партиями аналогичным образом (найдите делитель и т. Д.)
Партия, которая не набрала определенный процент голосов (например, 3,25%), автоматически получает 0 мест, и ее голоса не учитываются как «коалиция».
Вызов
Тебе дали :
- Список списков, каждый из вложенных списков содержит целые числа (количество голосов) и имеет длину 1 для одной партии или длину 2 для «коалиции».
- Минимальный процент голосов (он же «бар» за «заграждение»), чтобы получить места, в виде дроби (таким образом, 3,25% дается как 0,0325)
- Общее количество мест, которое будет распределено между всеми сторонами (целое число)
Вы должны распечатать ту же структуру вложенного списка, с количеством голосов, замененных местами в парламенте.
Победитель - это код с наименьшим количеством байтов.
Угловые чехлы:
- Там может (и обычно будет) более одного возможного делителя. Поскольку его нет в выводе, это не имеет значения.
- Представьте себе,
N=10
иns = [[1]]
, таким образом, делитель может быть 0,1 (не целое число) - В некоторых случаях не может быть решена, например
ns=[[30],[30],[100]]
,bar=0
,N=20
. Существует граница, сd=7.5
которой сумма скачанных значений возрастает с 19 до 21. Вы не должны решать эти случаи. (спасибо члену сообщества Арно, который указал на это дело)
Пример ввода и вывода
Очень неоптимизированный пример Python3:
from math import floor
def main(_l, bar, N):
# sum all votes to calculate bar in votes
votes = sum(sum(_) for _ in _l)
# nullify all parties that didn't pass the bar
_l = [[__ if __ >= bar * votes else 0 for __ in _] for _ in _l]
# find divisor for all parliament seats
divisor = find_divisor([sum(_) for _ in _l], N)
# find divisor for each 'coalition'
divisors = [find_divisor(_, floor(sum(_)/divisor)) for _ in _l]
# return final results
return [[floor(___/_) for ___ in __] for _, __ in zip(divisors, _l)]
def find_divisor(_l, N, _min=0, _max=1):
s = sum(floor(_ / _max) for _ in _l)
if s == N:
return _max
elif s < N:
return find_divisor(_l, N, _min, (_max + _min) / 2)
else:
return find_divisor(_l, N, _max, _max * 2)
print(main(l, bar, N))
Пример ввода:
l = [[190970, 156473],
[138598, 173004],
[143666, 193442],
[1140370, 159468],
[258275, 249049],
[624, 819],
[1125881],
[152756],
[118031],
[74701]]
bar = 0.0325
N = 120
И его вывод:
[[6, 4], [0, 5], [4, 6], [35, 5], [8, 8], [0, 0], [35], [4], [0], [0]]
Еще несколько примеров выходных данных:
Если bar=0.1
мы получим интересное противостояние между двумя сторонами, так как ни одна из меньших сторон не учитывается в:
[[0, 0], [0, 0], [0, 0], [60, 0], [0, 0], [0, 0], [60], [0], [0], [0]]
И если N=0
(угловой случай), то, конечно, никто ничего не получает:
[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0], [0], [0], [0]]
d=7.5
вы получаете прыжок с 19 до 21 места.Ответы:
05AB1E ,
4239 байтПопробуйте онлайн!
05AB1E не хватает хорошей рекурсии, поэтому реализация бинарного поиска, как в ссылочном коде, была бы болезненной. К счастью, нам вообще не нужно искать делитель!
Давайте рассмотрим простой пример: [600, 379, 12, 9] голосов, 100 мест, нет коалиций, нет бара. Сначала мы вычисляем, сколько дробных мест получает каждая сторона, определяя дробные места как
party_votes * seats / sum_of_votes
. В нашем примере это дает [60, 37,9, 1,2, 0,9].Интересным моментом является то, что если партия получает
f
дробные места, она получает либо реальные,int(f)
либоint(f) + 1
реальные места. Это означает, что мы уже знаем, как будет распределено 60 + 37 + 1 = 98 мест, и у нас осталось 2 «бонусных места» для распределения между 4 сторонами (ни одна из сторон не может получить более 1 бонусного места). Кому предоставляются эти бонусные места? Стороны с наибольшим соотношениемf / (int(f) + 1)
(доказательство оставлено в качестве упражнения для читателя). В наших примерах соотношения таковы[0.98, 0.997, 0.6, 0.9]
, что первые две партии получают бонусное место для каждой.Давайте посмотрим на код. Сначала мы заменяем счетчик голосов всех партий, которые не соблюдали планку, на 0:
Теперь, чтобы обойти отсутствие рекурсии, мы используем awkard
2F
для повторения основного кода дважды. На первом проходе он распределяет все места между коалициями, а на втором проходе он распределяет места каждой коалиции между ее партиями. Поскольку первый проход выполняется только один раз, а второй проходит для каждой коалиции, это предполагает довольно большую занятую работу.Хорошо, после этого непонятного бита вершина стека теперь является списком голосов (голоса коалиции на первом проходе, партия голосует на втором), а ниже это количество мест, которые нужно выделить. Мы используем это для вычисления списка фракционных мест:
Теперь мы вычисляем соотношения:
Побит не работает красиво, здесь. Он усекает целое число, добавляет 1 и отрицает все в одном байте. Зачем отрицать? В 05AB1E деление на 0 возвращает 0, и нам нужно, чтобы они сортировались последними.
D {# отсортированная копия соотношений ® 1% # дробных голосов мод 1 (иначе десятичные части) O # сумма вышеупомянутого (это количество бонусных мест) ò # раунд до ближайшего (требуется из-за числа с плавающей запятой bs) è # индекс в отсортированных соотношениях
Это дает нам (n + 1) -й лучший коэффициент, где n - количество бонусных мест (+1, потому что индексирование основано на 0). Таким образом, стороны, которые получают бонусное место, - это те, чье соотношение строго меньше этого.
источник
Python 2 , 220 байт
Попробуйте онлайн!
В основном просто гольф эталонной реализации ...
источник
Желе ,
6336 байтПопробуйте онлайн!
Полная программа, принимающая три аргумента: количество голосов в формате, описанном вопросом, планка и N в указанном порядке. Возвращает список списков мест. Нижний колонтитул на TIO просто для того, чтобы выделить структуру списка вывода. (В противном случае желе прячется
[]
за списки из одного элемента.)объяснение
Оригинальная подача (больше, но эффективнее)
Желе , 63 байта
Попробуйте онлайн!
источник
Вольфрам - без гольфа
Было просто любопытно решить ее с помощью LinearProgramming , а не кандидата в гольф, но, возможно, интересный подход к проблеме:
Прочитайте объяснение и попробуйте!
источник