|
Очередная задачка с codewars | ☑ | ||
---|---|---|---|---|
0
Kassern
26.12.20
✎
13:02
|
Добрый день форумчане. Вам скучно и одиноко, тогда у меня есть задачка для вас! Время от времени прохожу задачки на codewars на питоне/джаве чисто ради интереса.
Нашел вот такую задачку, кому интересно может попробовать решить в 1с. Хотелось бы увидеть красивый, читаемый код без лишних циклов/переменных. Дан n x n массив, верните элементы массива, расположенные от самых внешних элементов к среднему элементу, перемещаясь по часовой стрелке. Наглядный пример https://www.haan.lu/files/2513/8347/2456/snail.png Массив= [[1,2,3], [8,9,4], [7,6,5]] Результат = Массив [1,2,3,4,5,6,7,8,9] Проще говоря надо написать функцию ПолучитьМассивПоЗмейке(ДвумерныйМассив) которая вернет одномерный массив с элементами в нужном порядке. |
|||
1
acht
26.12.20
✎
13:11
|
(0) Вам скучно и одиноко
Не, чувак, это тебе - скучно и одиноко. Все кончится опять твоим вяленьким "вечерком сравню по времени выполнения", как в Оптимальное решение задачки |
|||
2
Ненавижу 1С
гуру
26.12.20
✎
13:15
|
(0) в случае четного n почему начало отсчёта берётся так как на рисунке?
|
|||
3
Ненавижу 1С
гуру
26.12.20
✎
13:17
|
(2) а не... Там наоборот внутрь идут
|
|||
4
Kassern
26.12.20
✎
13:19
|
(1) Только что проверил ваш код, немного замотался на наделе, при большом массиве покупателей ответ ваш код дает не верный и выполняется дольше. Отпишусь в том посте о результатах.
|
|||
5
Вафель
26.12.20
✎
13:20
|
вообще по идее можно в 1 цикле сделать
|
|||
6
Kassern
26.12.20
✎
13:20
|
(3) совершенно верно с внешних элементов во внутрь по часовой стрелке
|
|||
7
Kassern
26.12.20
✎
13:21
|
(5) хотелось бы увидеть этот один цикл) у меня так не получилось
|
|||
8
Ненавижу 1С
гуру
26.12.20
✎
13:23
|
Ну короче, там всё время два прохода одной длины. Потом длина уменьшается на 1. Между проходами поворот. В первый раз три прохода.
|
|||
9
acht
26.12.20
✎
13:26
|
(4) > при большом массиве покупателей
О. А давай-ка ты и для этой задачи, превентивно, какие-нибудь условия озвучишь. |
|||
10
Kassern
26.12.20
✎
13:31
|
(9) Лучше это писать в той ветке. По существу " Ваша задача - написать функцию для расчета общего времени, необходимого для обслуживания всех клиентов! " ваш код при данном массиве покупателей и касс дал не верный результат. Пример с 6тью кассами и заданным порядком покупателей я написал лишь для лучшего понимания задачи подсчета времени, не более того.
|
|||
11
Kassern
26.12.20
✎
13:33
|
(10) так же и здесь пример написан лишь для понимания задачки, матрица может быть и 10на10
|
|||
12
acht
26.12.20
✎
13:38
|
(11) Не пойдет. Я сейчас напишу тебе красивое и быстрое решение на каких-нибудь битовых операциях и услышу в ответ нудятину "а вот оно на больших размерностях..."
|
|||
13
acht
26.12.20
✎
13:40
|
(7) https://www.haikson.com/programmirovanie/zapolnenie-dvumernoj-matritsyi-po-spirali/
Заменяешь присвоение a[i][j] = k; на вывод этого a[i][j] и все |
|||
14
Kassern
26.12.20
✎
13:42
|
(13) А если не гуглить, а самому попытаться найти решение? Это ведь задачки, как раз развивать мышление.
|
|||
15
Kassern
26.12.20
✎
13:44
|
(14) я когда ее прорешивал у меня вот такой код получился, он и близко не идеальный, но рабочий:
def snail(snail_map): result=[]; array_len=len(snail_map); if array_len==0 or len(snail_map[0])==0: return result; array_vektor=[]; for i in range(array_len): if i==0: array_vektor.append(array_len); else: array_vektor.append(array_len-i); array_vektor.append(array_len-i); index=0; left_index=0; right_index=array_len-1; for v in array_vektor: if index==0: array=snail_map[left_index]; for i in range(v): k=i+left_index; result.append(array[k]); if index==1: for i in range(v): k=i+1+left_index; array=snail_map[k]; result.append(array[array_len-1-left_index]); if index==2: array=snail_map[right_index]; for i in range(v): k=array_len-i-2-left_index; result.append(array[k]); if index==3: for i in range(v): k=array_len-i-2-left_index; array=snail_map[k]; result.append(array[0+left_index]); index+=1; if index>3: index=0; left_index+=1; right_index-=1; return result; |
|||
16
Kassern
26.12.20
✎
13:46
|
(15) я более чем уверен что можно в 1с куда красивее это дело решить
|
|||
17
Salimbek
26.12.20
✎
13:47
|
инициализируем
delta=1 x=-1 y=0 lxy=n крутим цикл while (lxy>0){ for(i=0;i<lxy;i++){ x+=delta print Массив[x,y] } lxy-- if(lxy>0){ for(i=0;i<lxy;i++){ y+=delta print Массив[x,y] } } delta=-delta } |
|||
18
Salimbek
26.12.20
✎
13:50
|
+(17) Хотя if не нужен, все равно в цикл не зайдет
|
|||
19
RomanYS
26.12.20
✎
14:19
|
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ 1 КАК x, 0 КАК y |ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 0, 1 |ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ -1, 0 |ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 0, -1"; Векторы = Запрос.Выполнить().Выгрузить(); Слоев = Цел(N/2); НомерЯчейки = 0; Для Слой = 1 По Слоев Цикл Тек = Новый Структура("x,y", Слой, Слой); Для каждого Направление Из Векторы Цикл Для инд = 1 По N+1-2*Слой Цикл НомерЯчейки = НомерЯчейки + 1; ТД.Область(тек.y, тек.x).Текст = НомерЯчейки; тек.x = тек.x + Направление.x; тек.y = тек.y + Направление.y; КонецЦикла; КонецЦикла; КонецЦикла; Если N%2=1 Тогда НомерЯчейки = НомерЯчейки + 1; ТД.Область((N+1)/2, (N+1)/2).Текст = НомерЯчейки; КонецЕсли; |
|||
20
Kassern
26.12.20
✎
14:33
|
(19) прикольное решение, рисует в табличном документе числа в виде змейки. Только вот нужно обратное, уже готовый вложенный массив обойти и получить числа в нужном порядке в виде массива. Скорее всего нужно немного подправить ваш код и он это сделает. Например заполнить табдок начальными массивами, а потом в массив получить текст из ячеек в вашем цикле.
|
|||
21
RomanYS
26.12.20
✎
14:38
|
(20) Да, конечно нужно одну строку поменять
ТД.Область(тек.y, тек.x).Текст = НомерЯчейки; на МассивЛинейный[НомерЯчейки-1] = МассивКвадратный[тек.y-1][тек.x-1];//вомзожно x и y нужно поменять местами Хотелось без "если" обойтись, но не получилось) |
|||
22
ДедМорроз
26.12.20
✎
14:55
|
Самый простой вариант - создаём массив битов,равный нашему массиву.
Когда проходим точку,то устанавливаем бит. Далее,определяем смещение по x и y для перехода в следующую точку. Прежде чем шагнуть,проверяем,а не занята ли она,если занята,то меняем вектор смещения поворотом по часовой стрелке. Когда сразу после поворота снова будет занято,то мы прошли весь массив. |
|||
23
Конструктор1С
26.12.20
✎
15:20
|
(15) вот зачем писать такой стрёмный код? Хотя бы разбей метод на подметоды
|
|||
24
Kassern
26.12.20
✎
15:25
|
(23) Писал на время и не особо парился за красоту, сам принцип был интересен. Да и питон для меня в новинку, только начал изучать.
|
|||
25
Kassern
26.12.20
✎
15:27
|
(23) вот тебе красивый код:
def snail(array): ret = [] if array and array[0]: size = len(array) for n in xrange((size + 1) // 2): for x in xrange(n, size - n): ret.append(array[n][x]) for y in xrange(1 + n, size - n): ret.append(array[y][-1 - n]) for x in xrange(2 + n, size - n + 1): ret.append(array[-1 - n][-x]) for y in xrange(2 + n, size - n): ret.append(array[-y][n]) return ret |
|||
26
Конструктор1С
26.12.20
✎
16:48
|
(25) код стал короче, но не стал понятнее. Низачот
|
|||
27
Вафель
26.12.20
✎
18:35
|
тут 4 цикла по ребрам и 1 общий цикл по числу кругов
|
|||
28
Garykom
гуру
26.12.20
✎
18:40
|
(0) Простейший КА же
|
|||
29
Garykom
гуру
26.12.20
✎
18:46
|
(28)+ Две переменные нужны номер шага и направление исполнителя и текущие координаты x, y
Изначально шаг = 0, направление 1 (принимаем что 0 - вверх, 1 вправо, 2 вниз, 3 влево), координаты 0,0 левый верхний угол Далее на каждом шагу считываем текущее значение x,y из массива, если можно то перемещаемся на 1 шаг в текущем направлении, пройденную ячейку массива помечаем как то (можно новым массив NxN завести или в текущий нечто писать) Если упираемся в "стену" (границы массива или уже пройденное значение) то сначала поворот на 90 по часовой а затем уже шаг перед, если и там стена то снова поворот Как уже некуда идти после поворотов то стоп |
|||
30
Cthulhu
27.12.20
✎
02:16
|
|
|||
31
Конструктор1С
27.12.20
✎
05:16
|
(30) эм... Хуже такого кода разве что вот такой:
https://www.govnokod.ru/1777 и мозг сломаешь, пытаясь понять логику работы кода, и для трудновыявляемых ошибок благодатная среда |
|||
32
Документовед
27.12.20
✎
08:07
|
Процедура ВыводМассиваИсточник(ВысотаМасива, ДлинаМасива, МассивИсточник)
Для НомВысота = 1 по ВысотаМасива Цикл ТекСтрока = ""; Для НомДлина = 1 по ДлинаМасива Цикл ТекЗначСтр = СокрЛП(МассивИсточник[НомДлина - 1][НомВысота - 1]); ТекСтрока = ТекСтрока + ЛЕв(" ", 5 - СтрДлина(ТекЗначСтр))+ ТекЗначСтр +"; "; КонецЦикла; Сообщить(ТекСтрока); КонецЦикла; КонецПроцедуры Процедура ВыводМассиваРезультат(ВысотаМасива, ДлинаМасива, МассивРезультат) ТекСтрока = ""; Для НомДлина = 1 по ДлинаМасива*ВысотаМасива Цикл ТекЗначСтр = СокрЛП(МассивРезультат[НомДлина - 1]); ТекСтрока = ТекСтрока + ЛЕв(" ", 5 - СтрДлина(ТекЗначСтр))+ ТекЗначСтр +"; "; КонецЦикла; Сообщить(ТекСтрока); КонецПроцедуры ДлинаМасива = 5; ВысотаМасива = 5; МассивИсточник = Новый Массив(ДлинаМасива, ВысотаМасива); МассивРезультат = Новый Массив(ДлинаМасива * ВысотаМасива); НаправлениеДвиженияГоризнотальное = Истина; ТекСтолбец = 0; ТекСтрока = 1; НаправлениеДвиженияПоДлине = 1; НаправлениеДвиженияПоВысоте = 1; ПройденоКолец = 0; Для НомПП = 1 По ДлинаМасива * ВысотаМасива Цикл Если НаправлениеДвиженияГоризнотальное Тогда ТекСтолбец = ТекСтолбец + НаправлениеДвиженияПоДлине; Если (НаправлениеДвиженияПоДлине > 0 и ТекСтолбец = ДлинаМасива - ПройденоКолец ) или (НаправлениеДвиженияПоДлине < 0 и ТекСтолбец = 1 + ПройденоКолец) Тогда НаправлениеДвиженияГоризнотальное = не НаправлениеДвиженияГоризнотальное; НаправлениеДвиженияПоДлине = - НаправлениеДвиженияПоДлине; Если НаправлениеДвиженияПоДлине >0 ТОгда ПройденоКолец = ПройденоКолец +1; КонецЕСли; КонецЕСли; Иначе ТекСтрока = ТекСтрока + НаправлениеДвиженияПоВысоте; Если (НаправлениеДвиженияПоВысоте > 0 и ТекСтрока = ВысотаМасива - ПройденоКолец ) или (НаправлениеДвиженияПоВысоте < 0 и ТекСтрока = 1 + ПройденоКолец) Тогда НаправлениеДвиженияГоризнотальное = не НаправлениеДвиженияГоризнотальное; НаправлениеДвиженияПоВысоте = - НаправлениеДвиженияПоВысоте; КонецЕСли; КонецЕСли; МассивИсточник[ТекСтолбец - 1][ТекСтрока - 1] = НомПП; МассивРезультат[ДлинаМасива * ВысотаМасива - НомПП] = НомПП; КонецЦикла; ВыводМассиваИсточник(ВысотаМасива, ДлинаМасива, МассивИсточник); Сообщить(" "); ВыводМассиваРезультат(ВысотаМасива, ДлинаМасива, МассивРезультат); |
|||
33
Доктор Манхэттен
27.12.20
✎
09:29
|
(0) Решение на JS. Проверка граничных условий получилась довольно длинная, вынес в отдельные функции checkX и checkY:
function snail(a) { let x = -1, y = dy = 0, dx = 1, res = [], n = a.length; const checkX = () => dx === 0 || x + dx !== (dx > 0 ? n - y : n - y - 2); const checkY = () => dy === 0 || y + dy !== (dy > 0 ? x + 1 : x); do { do { res.push(a[y += dy][x += dx]); } while(checkX() && checkY()); [dy, dx] = [dx, -dy]; } while (checkX() && checkY()) return res; } |
|||
34
Доктор Манхэттен
27.12.20
✎
09:37
|
+(33) Заметьте, без каких-либо счетчиков циклов. Чистая логика. Сам себе решил усложнить задачу, иначе слишком легко и не интересно
|
|||
35
Документовед
27.12.20
✎
09:57
|
С рекурсией
Процедура ВыводМассиваИсточник(ВысотаМасива, ДлинаМасива, МассивИсточник) Для НомВысота = 1 по ВысотаМасива Цикл ТекСтрока = ""; Для НомДлина = 1 по ДлинаМасива Цикл ТекЗначСтр = СокрЛП(МассивИсточник[НомДлина - 1][НомВысота - 1]); ТекСтрока = ТекСтрока + ЛЕв(" ", 5 - СтрДлина(ТекЗначСтр))+ ТекЗначСтр +"; "; КонецЦикла; Сообщить(ТекСтрока); КонецЦикла; КонецПроцедуры Процедура ВыводМассиваРезультат(ВысотаМасива, ДлинаМасива, МассивРезультат) ТекСтрока = ""; Для НомДлина = 1 по ДлинаМасива*ВысотаМасива Цикл ТекЗначСтр = СокрЛП(МассивРезультат[НомДлина - 1]); ТекСтрока = ТекСтрока + ЛЕв(" ", 5 - СтрДлина(ТекЗначСтр))+ ТекЗначСтр +"; "; КонецЦикла; Сообщить(ТекСтрока); КонецПроцедуры Процедура ЗаполнитьПолниМассив(МассивИсточник, МассивРезультат = Неопределено, НомПП = 0, струкДвижения = Неопределено, ПройденоКолец = 0, ИмяТекКоординаты = "ТекСтолбец", ИмяТекГраницы = "ДлинаМасива", НаправлениеДвижения = 1) Если НомПП > струкДвижения.ВысотаМасива * струкДвижения.ДлинаМасива Тогда Возврат; КонецЕСли; струкДвижения[ИмяТекКоординаты] = струкДвижения[ИмяТекКоординаты] + НаправлениеДвижения; Если (НаправлениеДвижения > 0 и струкДвижения[ИмяТекКоординаты] = струкДвижения[ИмяТекГраницы] - ПройденоКолец ) или (НаправлениеДвижения < 0 и струкДвижения[ИмяТекКоординаты] = 1 + ПройденоКолец) Тогда Если ИмяТекКоординаты = "ТекСтрока" Тогда НаправлениеДвижения = - НаправлениеДвижения; Если НаправлениеДвижения > 0 Тогда ПройденоКолец = ПройденоКолец +1; струкДвижения.ТекСтолбец = струкДвижения.ТекСтолбец + 1; струкДвижения.ТекСтрока = струкДвижения.ТекСтрока + 1; КонецЕСли; КонецЕСли; ИмяТекКоординаты = ?(ИмяТекКоординаты = "ТекСтолбец", "ТекСтрока", "ТекСтолбец" ); ИмяТекГраницы = ?(ИмяТекГраницы = "ДлинаМасива", "ВысотаМасива", "ДлинаМасива" ); КонецЕСли; МассивИсточник[струкДвижения.ТекСтолбец - 1][струкДвижения.ТекСтрока - 1] = НомПП; МассивРезультат[струкДвижения.ДлинаМасива * струкДвижения.ВысотаМасива - НомПП] = МассивИсточник[струкДвижения.ТекСтолбец - 1][струкДвижения.ТекСтрока - 1]; ЗаполнитьПолниМассив(МассивИсточник, МассивРезультат, НомПП+ 1, струкДвижения, ПройденоКолец, ИмяТекКоординаты, ИмяТекГраницы, НаправлениеДвижения) КонецПроцедуры Функция ПолучитьМассивПоЗмейке(МассивИсточник) струкДвижения = Новый Структура; струкДвижения.Вставить("ТекСтолбец", 0); струкДвижения.Вставить("ТекСтрока", 1); струкДвижения.Вставить("ВысотаМасива", 0); струкДвижения.Вставить("ДлинаМасива", 0); Попытка струкДвижения.ВысотаМасива = МассивИсточник.Количество(); струкДвижения.ДлинаМасива = МассивИсточник[0].Количество(); Исключение Сообщить("Чёт, не похоже на двумерный массив."); Возврат Новый Массив(1); КонецПопытки; МассивРезультат = Новый Массив(струкДвижения.ДлинаМасива * струкДвижения.ВысотаМасива); НомПП = 1; ЗаполнитьПолниМассив(МассивИсточник, МассивРезультат, 1, струкДвижения, 0, "ТекСтолбец", "ДлинаМасива", 1); Возврат МассивРезультат; КонецФункции ДлинаМасива = 5; ВысотаМасива = 5; МассивИсточник = Новый Массив(ДлинаМасива, ВысотаМасива); МассивРезультат = ПолучитьМассивПоЗмейке(МассивИсточник); ВыводМассиваИсточник(ВысотаМасива, ДлинаМасива, МассивИсточник); Сообщить(" "); ВыводМассиваРезультат(ВысотаМасива, ДлинаМасива, МассивРезультат); |
|||
36
Документовед
27.12.20
✎
10:08
|
(35) Поправка
струкДвижения.ДлинаМасива = МассивИсточник.Количество(); струкДвижения.ВысотаМасива = МассивИсточник[0].Количество(); |
|||
37
DrHiHi
27.12.20
✎
14:08
|
еще вариант автокода))
Массив = ПолучитьМассив(); // задаваемый массив хмакс = Массив.ВГраница(); умакс = хмакс; хмин = 0; умин = 0; угол = 0; х = 0; у = 0; МассивРезультат = Новый Массив; Пока Истина Цикл МассивРезультат.Добавить(Массив[у][х]); Если угол = 0 Тогда Если х = хмакс Тогда угол = 1; умин = умин + 1; у = умин; Иначе х = х + 1; КонецЕсли; ИначеЕсли угол = 1 Тогда Если у = умакс Тогда угол = 2; хмакс = хмакс - 1; х = хмакс; Иначе у = у + 1; КонецЕсли; ИначеЕсли угол = 2 Тогда Если х = хмин Тогда угол = 3; умакс = умакс - 1; у = умакс; Иначе х = х - 1; КонецЕсли; ИначеЕсли угол = 3 Тогда Если у = умин Тогда угол = 0; хмин = хмин + 1; х = хмин; Иначе у = у - 1; КонецЕсли; КонецЕсли; Если умин > умакс Или хмин > хмакс Тогда Прервать; КонецЕсли; КонецЦикла; |
|||
38
Cthulhu
27.12.20
✎
14:11
|
(31) спасибо ваше мнение очень важно для нас (с) lol
если мозгов не хватает понять обычную рекурсию и элементарную арифметику - так работайте над собой а не нойте про говнокод. ))) |
|||
39
Конструктор1С
27.12.20
✎
19:09
|
(38) 一个小气的财主叫他的仆人去买酒,但是没有给他钱。仆人说:“老爷,没有钱怎么能够买酒呢?”财主生气地说:“用钱买酒谁不会?不用钱买到酒才算真的能干呢!”
过了一会儿,仆人拿着空瓶子回来了。主人大骂:“你叫我喝什么?”仆人说:“瓶子里没有酒,谁不会喝!要是能从空瓶子里喝出酒来,那才算真的能干呢!” если мозгов нет понять иероглифы, так работайте над собой. Код пишется для людей, а не для компилятора. Хороший код должен быть максимально линейным и понятным. А не состоять из шарад в виде рекурсий |
|||
40
Конструктор1С
27.12.20
✎
19:15
|
Всё просто. Допустим, написал ты 100 строк кода. Другому программисту нужно внести небольшие изменения в этот код:
- если программист за 2 минуты понял всю логику кода и сразу же нашел место правки - у тебя хороший код - если программисту понадобилось полчаса ползать с отладчиком по твоему коду - у тебя дрянной код |
|||
41
bolder
27.12.20
✎
19:18
|
Код в (30) и (37) выдает ошибку даже на массиве 3×3.Можно дальше не смотреть.
|
|||
42
Salimbek
27.12.20
✎
20:46
|
(41) А в (17)?
|
|||
43
Cthulhu
27.12.20
✎
20:58
|
(41): врете, код в (30) скопирован из рабочей обработки и не дает ошибок.
жду извинений. |
|||
44
bolder
27.12.20
✎
21:21
|
(43) Извиняюсь, код (30) правильно работает до массивов 40x40, даже меньше. Код (37) работает без проблем и на массиве 100x100.Что впрочем и понятно - нерекурсивный.Можно написать еще проще.
|
|||
45
acht
27.12.20
✎
21:33
|
(0) Это вот то самое, что ты хотел добиться - локального срачика?
|
|||
46
bolder
27.12.20
✎
22:14
|
(42) (17)Работает, но наоборот - против часовой стрелки раскручивает.
|
|||
47
Salimbek
28.12.20
✎
00:42
|
(46) А... ясно, Массив[x,y] - неправильно, надо везде поменять на Массив[y,x] - первым идет номер слоя (т.е. координата y), а вторым - позиция элемента в этом слое (т.е. координата x)
|
|||
48
Доктор Манхэттен
28.12.20
✎
02:19
|
(44) >> Код (37) работает без проблем и на массиве 100x100
Как-то слабовато. Щас проверил код из (33), на массиве 10000х10000 отработал за несколько секунд в браузере Эдж. В Хроме или в Ноде должен еще быстрее. |
|||
49
Доктор Манхэттен
28.12.20
✎
05:51
|
Немного оптимизировал по времени выполнения
Максимальный размер массива получился 46340 х 46340, выборал исходя из размера занимаемой памяти, чтобы входной и выходной массивы были примерно по 2 гигабайта. Браузер не дает выделять больше памяти. На тестовом массиве функция выполняется за пол минуты в Microsoft Edge. В хроме не пробовал, лень устанавливать. Проверьте у кого есть. function snail(a) { let x = -1, y = dy = resIndex = 0, dx = 1; const n = a.length; const res = new Uint8Array(n * n); for (let l = n - .5; l; l -= .5) { for (let i = 0; i < l; i++) res[resIndex++] = a[y += dy][x += dx]; [dy, dx] = [dx, -dy]; }; return res; } |
|||
50
Kassern
28.12.20
✎
09:06
|
(45) Я лишь хотел поделиться интересной на мой взгляд задачкой, не более. Я вообще удивлен, что столько людей так же заинтересовались и попробовали решить. Всем спасибо
|
|||
51
mikecool
28.12.20
✎
09:12
|
вот вам задачка для питона(я не могу результат в секунду уложить):
Штаб гражданской обороны Тридесятой области решил обновить план спасения на случай ядерной атаки. Известно, что все n селений Тридесятой области находятся вдоль одной прямой дороги. Вдоль дороги также расположены m бомбоубежищ, в которых жители селений могут укрыться на случай ядерной атаки. Чтобы спасение в случае ядерной тревоги проходило как можно эффективнее, необходимо для каждого селения определить ближайшее к нему бомбоубежище. Формат ввода В первой строке вводится число n - количество селений (1 <= n <= 100000). Вторая строка содержит n различных целых чисел, i-е из этих чисел задает расстояние от начала дороги до i-го селения. В третьей строке входных данных задается число m - количество бомбоубежищ (1 <= m <= 100000). Четвертая строка содержит m различных целых чисел, i-е из этих чисел задает расстояние от начала дороги до i-го бомбоубежища. Все расстояния положительны и не превышают 10⁹. Селение и убежище могут располагаться в одной точке. Формат вывода Выведите 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 |
|||
52
mikecool
28.12.20
✎
09:13
|
+51 ограничение - память 65535, время - 1000мс
|
|||
53
Доктор Манхэттен
28.12.20
✎
09:42
|
(52) >> количество селений (1 <= n <= 100000)
>> ограничение - память 65535 >> Для хранения ответа используйте список, где индекс будет номером селения Что-то тут не сходится... |
|||
54
mikecool
28.12.20
✎
10:01
|
(53) вот тут я хз, но мое решение работает, только по времени исполнения ошибку выдает...
|
|||
55
Salimbek
28.12.20
✎
15:13
|
(51) А как делал?
Если идти по подсказке, то: 1) сортируем список селений и бомбоубежищ 2) Допустим мы рассмотрели селение i, и для него нашли ближайшее убежище j. Теперь берем следующее селение i+1 и для него надо быстро найти ближайшее бомбоубежище. Оно будет либо полученное на предыдущем шаге (j), либо какое-то из последующих (j...n) 3) Используем Метод половинного деления, т.е. берем середину нашего отрезка k=(j+n)/2 и смотрим - селение находится ближе или дальше этой середины. Если ближе, то рассматриваем диапазон (j...k), если дальше, то диапазон (k...n). P.S. Мне лениво разбираться, когда остается 1-2 элемента, поэтому если в выборке осталось менее 5 элементов, то я делаю просто полный перебор, все равно это выгоднее, чем перебирать все 100000 элементов. |
|||
56
fisher
28.12.20
✎
16:40
|
(49) Норм. В (17) почти тоже самое, но у тебя доведено. Вероятно, оптимальный вариант. Только пляски с 0.5 - ну, такое... Вкусовщина, но лично я не одобрям.
|
|||
57
bolder
28.12.20
✎
18:51
|
(48) Хм..Как то тоже слабовато.Массив 100000x 100000 ( 10 в 10 степени ячеек) основное время - заполнение первоначального массива. Сама процедура, без вывода результата ;-) - мгновенно.
Полное время 1 минута 47 с. А сколько времени в браeзере занимает вывод массива 40000x40000? |
|||
58
Доктор Манхэттен
29.12.20
✎
00:00
|
(56) 0.5 мне тоже не очень, можно было заменить на какой-то флаг и добавить пару условий и дополнительных действий, но это мало влияет, так что не стал.
|
|||
59
Доктор Манхэттен
29.12.20
✎
00:06
|
(57) Странно. У меня первоначальный массив заполнялся гораздо быстрее чем основная процедура. Ты как заполнял?
Вывод массива в браузер не происходит, не стал это оформлять в какой-то формат или HTML. Результат выполнения функции возвращается в консоль, его можно посмотреть в виде массива в удобном виде. |
|||
60
Доктор Манхэттен
29.12.20
✎
00:06
|
100000x100000 - мгновенно? Не верю что-то.
|
|||
61
_DAle_
29.12.20
✎
00:10
|
(55) В третьем пункте не нужен двоичный поиск. Нужно просто для каждого селения i двигать вправо j пока расстояние между селением i и бомбоубежищем j уменьшается.
|
|||
62
Доктор Манхэттен
29.12.20
✎
01:43
|
(61) В самой задаче уже все расписано как ее делать. Так не интересно.
|
|||
63
Salimbek
29.12.20
✎
07:58
|
(61) Ну... автор этой темы в (54) говорит, что у него не проходит алгоритм по времени.
|
|||
64
Доктор Манхэттен
29.12.20
✎
08:36
|
(63) Проверил этот алгоритм на JS в браузере, результат вполне быстрый, ~200 миллисекунд на максимальных размерах массивов:
function bomb(cities, vaults) { const [ sortedCities, sortedVaults ] = [ cities, vaults ].map(coords => Object.entries(coords).sort((a, b) => a[1] - b[1])) let vaultIndex = 0; const vaultsCount = vaults.length; const ret = new Uint32Array(cities.length); sortedCities.map(city => { const cityCoord = city[1]; let closestDisd = Math.abs(cityCoord - sortedVaults[vaultIndex][1]); let nextDist; while (vaultIndex + 1 < vaultsCount && (nextDist = Math.abs(cityCoord - sortedVaults[vaultIndex + 1][1])) < closestDisd) { vaultIndex++; closestDisd = nextDist; } ret[city[0]] = +sortedVaults[vaultIndex][0] + 1; }); return ret; } Причем самая долгая операция - это сортировка в самой первой строке: ~180 миллисекунд. Основной цикл выполняется в среднем за 20-25 миллисекунд. |
|||
65
Доктор Манхэттен
29.12.20
✎
08:41
|
Как я понимаю, суть задачи - это написать свою более быструю функцию сортировки. В ней как раз узкое место. Но мы, конечно, этого делать не будем, потому что это уже делали на лабораторных работах в ВУЗе, ничего интересного, все уже давно придумано.
|
|||
66
_DAle_
29.12.20
✎
10:51
|
(63) Да, но судя по всему у него как-то не так реализован последний шаг, потому что и вариант с двоичным поиском, и вариант с просто продвижением второго указателя (который практически полностью описан в самом условии) должны быть достаточны для такого размера входных данных. Более того с этими двумя вариантами самым затратным шагом будет все равно является сортировка.
Почему я сказал, что двоичный поиск не нужен. Смотрите, если мы делаем двоичный поиск для каждого селения, то сложность этой части алгоритма в худшем случае (а при решении таких задачек нас ведь интересует именно худший случай в отличие от реальной жизни) будет O(n*log(m)), сложность варианта с продвижением второго указателя - O(n+m), так как мы идем всегда только вправо и за весь внешний цикл в худшем случае просто пройдем весь массив бомбоубежищ. Второй вариант при данных ограничениях будет быстрее. Вариант с двоичным поиском имеет, конечно, право на существование, когда m на порядки больше n, тогда мы исключаем линейную зависимость от количества бомбоубежищ. Но все это довольно бесполезно анализировать, когда у нас первый шаг - это сортировка обоих массивов, которая уже сама по себе будет иметь сложность O(n*log(n) + m*log(m)), что и будет являться самой затратной по времени частью алгоритма. Вообще, эта задачка - достаточно классический пример two pointers technique. И ее основной смысл обычно в демонстрации этой незамысловатой техники. |
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |