"Гражданская оборона" - задание на python
Пытаюсь решить задание на Coursera. Вот условие:
Штаб гражданской обороны Тридесятой области решил обновить план спасения на случай ядерной атаки. Известно, что все n селений Тридесятой области находятся вдоль одной прямой дороги. Вдоль дороги также расположены m бомбоубежищ, в которых жители селений могут укрыться на случай ядерной атаки.
Чтобы спасение в случае ядерной тревоги проходило как можно эффективнее, необходимо для каждого селения определить ближайшее к нему бомбоубежище.
Формат вывода
Выведите n чисел - для каждого селения выведите номер ближайшего к нему бомбоубежища. Бомбоубежища пронумерованы от 1 до m в том порядке, в котором они заданы во входных данных.
Примеры
Тест 1
Входные данные:
4
1 2 6 10
2
7 3
Вывод программы:
2 2 1 1
Тест 2
Входные данные:
1
1
1
2
Вывод программы:
1
Тест 3
Входные данные:
10
79 64 13 8 38 29 58 20 56 17
10
53 19 20 85 82 39 58 46 51 69
Вывод программы:
5 10 2 2 6 3 7 3 7 2
Долго пытаюсь решить - ничего не получается. Решал через двойной цикл со сложностью алгоритма O(n^2) - не проходит по времени. Стал использовать бинарный поиск, но почему-то не получается. Вот мой код:
n = int(input())
villages = list(map(int, input().split()))
m = int(input())
shelter_range = input().split()
shelters = []
for i in range(m):
shelters.append((int(shelter_range[i]), i))
shelters.sort()
k = []
for i in range(n):
if villages[i] > shelters[-1][0]:
k.append(shelters[-1][1] + 1)
elif villages[i] < shelters[0][0]:
k.append(shelters[0][1] + 1)
else:
low = 0
high = len(shelters) - 1
while high - low > 1:
mid = (low + high) >> 1
if villages[i] > shelters[mid][0]:
low = mid
else:
high = mid
if villages[i] - shelters[low][0] < shelters[high][0] - villages[i]:
k.append(low + 1)
else:
k.append(high + 1)
print(*(k[i] for i in range(len(k))))
Нашёл решение с бинарным поиском - оно выдаёт 100/100:
n = int(input())
a = map(int, input().split())
m = int(input())
b = list(map(int, input().split()))
for i in range(len(b)):
b[i] = [i + 1, b[i]]
b.sort(key=lambda x: x[1])
def find_value(x):
if(x < b[0][1]):
return b[0][0]
if(x > b[-1][1]):
return b[-1][0]
l = 0
r = len(b) - 1
while(r - l > 1):
m = (r + l) >> 1
if(b[m][1] < x):
l = m
else:
r = m
if(x - b[l][1] < b[r][1] - x):
return b[l][0]
else:
return b[r][0]
print(*[find_value(v) for v in a])
Мой код выглядит примерно так же, за исключением наличия функции, но результаты выдаёт неверные. Вопрос: почему мой код не работает?
Ответы (1 шт):
Поправил ваш код - нашёл ошибку, заменил несколько однобуквенных имён переменных на говорящие. Строчки в которых были ошибки закомментированы.
Также частично сократил лишние действия, например
Заменил
for i in range(n): if villages[i]...на
for village in villages: if villageПереместил печать ответа в основной цикл, так как это больше подходит здесь, ведь в вашем решении, в отличии от рабочего примера, не используется функция:
print(answer, end=' ') print()
Исправленное решение
n = int(input())
villages = list(map(int, input().split()))
m = int(input())
shelter_range = input().split()
shelters = []
for i in range(m):
shelters.append((int(shelter_range[i]), i + 1))
shelters.sort()
for village in villages:
if village > shelters[-1][0]:
answer = shelters[-1][1]
elif village < shelters[0][0]:
answer = shelters[0][1]
else:
low = 0
high = len(shelters) - 1
while high - low > 1:
mid = (low + high) >> 1
if village > shelters[mid][0]:
low = mid
else:
high = mid
if village - shelters[low][0] < shelters[high][0] - village:
#answer = low
answer = shelters[low][1]
else:
#answer = high
answer = shelters[high][1]
print(answer, end=' ')
print()
Пара моих решений.
Интересно, какое работает быстрее. По асимптотике бинарный поиск должен работать быстрее, ведь в первом решении две сортировки против одной в бинарном поиске, однако обе выполняются встроенной функцией, а бинарный поиск делается виртуальной машиной.
1. Последовательный поиск
Сортируются оба исходных списка (поселения, бомбоубежища), затем поселения раскладываются в соответствии с вынимаемыми по очереди бомбоубежищами. Берём самое близкое бомбоубежище, смотрим какие туда идут поселения, начиная с самого близкого, если дошли до поселения выходящего за пределы данного бомбоубежища, переходим к следующему бомбоубежищу и т. д.
def get_input():
num = int(input())
lst = list(map(int, input().split()))
return num, lst, sorted(lst)
village_num, village_lst, sorted_village_lst = get_input()
shelter_num, shelter_lst, sorted_shelter_lst = get_input()
result = {}
# float('-inf'), float('inf') - отрицательная и положительная бесконечность.
# Использую, чтобы не делать проверку начала и конца списка
sorted_shelter_lst = [float('-inf'), *sorted_shelter_lst, float('inf')]
shelter_indexes = {shltr_name : shltr_idx for shltr_idx, shltr_name in enumerate(shelter_lst, 1)}
cnt = 0
for left, right in zip(sorted_shelter_lst, sorted_shelter_lst[1:]):
while cnt < len(sorted_village_lst):
village = sorted_village_lst[cnt]
if village <= left:
result[village] = shelter_indexes[left]
elif village < (left + right) / 2:
result[village] = shelter_indexes[left]
else:
break
cnt += 1
# Для ответа нужен исходный порядок поселений, который хранится в village_lst.
print(' '.join(str(result[village]) for village in village_lst))
2. Бинарный поиск
Похоже на ваше, но есть отличия. Например, храню исходные индексы бомбоубежищ в словаре.
def get_input():
num = int(input())
lst = list(map(int, input().split()))
return num, lst
def bin_search(village, shelters):
left = 0
right = len(shelters) - 1
while right - left > 1:
middle = (left + right) // 2
if village > shelters[middle]:
left = middle
else:
right = middle
shelter_idx = right if village > (shelters[left] + shelters[right]) // 2 else left
return shelters[shelter_idx]
village_num, village_lst = get_input()
shelter_num, shelter_lst = get_input()
sorted_shelter_lst = sorted(shelter_lst)
original_shelter_indexes = {shltr_name : shltr_idx for shltr_idx, shltr_name in enumerate(shelter_lst, 1)}
for village in village_lst:
shltr_name = bin_search(village, sorted_shelter_lst)
print(original_shelter_indexes[shltr_name], end=' ')
print()