Комментарии 72
7
найти самую длинную совпдающую подпоследовательность второго слова в первом и
приписать недостающие буквы с обоих сторон от крайних символов подпоследовавтельности
домножить все числа до самого старшего разряда в наборе. Запомнить кого на сколько помножили. Отсортировать по значению. Делить на запомненные ранее множители
вычесть B\A — по одному выкидывая элементы. Искать совпадения перебором от последнего выкинутого элемента, к примеру. Даст максимально быстрый результат если оба входных массива отсортированы (ну мало ли)
Ваше решение 1-задачи вызывает больше вопросов, чем дает ответы. Во-первых, поиск максимальной подпоследовательности — это не тривиальная задача. Даже найдя её, нужно ещё правильно расставить значения по бокам. В -третьих, что будет, если одна и та же максимальная подпоследовательность будет встречаться во втором слове несколько раз?
По описанному вами невозможно определить даже алгоритмическую сложность.
Ваше решение как минимум неполное, как максимум неверное. Неполнота не дают мне возможность построить контрпример.
Пожалуйста, распишите, что будет после каждого этапа вашего алгоритма при обработке слов ABCE и ACDE, получится ли в итоге слово ABCDE?
тогда будет такое: A нашлось, ищет совпадения дальше, до C, потом D не находит и оставляет метку конца, находит E, и вставляет метку начала, потом доходит до конца строки и видит что совпадений 3. Дальше даже не пытается начать поиск с B, поскольку это третий символ с конца слова1, больше чем 3 символа уже никак не получится, значит мы пришли к максимуму. Вызывается слово1, в нём метки указывают что между найденными соответсвующими C и E надо вставить кусок слова2 между метками слова2 (а там D). В итоге будет такое слово. Только надо метки писать — куда какой номер подошёл. Для небольших слов можно использовать для этого не массив меток вида (номер, конец или начало, соответствующий номер в другом слове), а маску где порядковый номер символа в одном слове записывается в порядковый номер, под которым он же находится в другом слове.
(6) — первая из каждой пятерки.
Получаем самую быструю + 2 кандидата (6.1 + 6.2) + выпадают все лошади из пятёрок тех, кто на 3, 4 и 5 местах.
Итого остаётся получить 2х самых быстрых из 6 лошадей (2 кандидата + места 2/3 из пятерок победителя и второго места), из которых нам только известно только про двух лошадей что одна быстрее другой (безотносительно остальных 4х). Еще 2 забега потому, имхо…
мне одному кажется что в applear нет подстроки pear?
Подмножеством не обязательно является цельная подстрока.
{a,p,l,e} — подмножество {a,e,l,p}? Да, ведь множество — набор без какого-либо порядка.
Тогда, задача тривиальна — выбираем все различные буквы из первого слова и из второго, объединяем эти множества, вот и ответ.
В оригинале речь шла о подпоследовательности.
Я счёл, что пример, взятый из оригинала, более корректно иллюстрирует условие, поэтому перевёл соответствующе.
O(NM), N=length(s1), M=length(s2)
Нужно рассмотреть ф-цию F(a,b), которая решает задачу на урезанных входных строках длины a и b, а затем вычислять F(a,b) для всех a=1..N, b=1..M, опираясь на предыдущие вычисленные F
Обычно в таких задачах даже можно уложиться по памяти не в O(N*M), а в O(max(N,M)), т.к. рекуррентная формула обращается только к предыдущей строке матрицы.
На собеседовании, наверное, я бы записал рекуррентное соотношение минут за 15-30, но сейчас думать лень )))
вопрос 2) 6 забегов
В 1 вопросе бактерия делением звонит пробирку за 60 секунд на 2^60. 4 бактерии заполнят: 2^60/4 и от этого числа взять логарифм по основанию 2
Задача 2) 25 лошадей делим на 5 лошадей в забеге и получаем 5 победителей, потом ещё 1 забег. Правда в этом случае не сработает если все хорошие лошади в одной пятерке.
Задача 2 вариант 2) из каждого забега брать по 3 лошади и формировать пятерки из них итого 12 забегов.
Состояния «в пробирке уже 4 бактерии» исходная особь достигает за 2 секунды (1->2->4), итого 60-2=58.
3 забега по 5 — выбираем по 3 лучших из каждого — осталось 9 лошадок
2 забега 5+4 — выбираем по 3 лучших — осталось 6
1 забег из 5 лошадей — 3 лучших идут в следующий забег
1 забег из 4х лошадей (3 из предыдущего и +1 отдыхающая лошадь) — по результатам определяем 3 быстрейших лошади
лошадки, конечно, должны бегать стабильно по своему максимальному результату и не уставать :)
Мои решения.
1) Подобная задача у нас была ещё в школе, классе примерно 8-м. И даже тогда она легко решалась.
2) Первое решение — это разбить всех на 5-ки. Пустить 5 забегов (узнать места внутри пятерки). Выбираем 5 лидеров. Затем пока не наберем нужное количество поступаем так: устраиваем забег, выбираем первую лошадь, и на её место ставим следующую лошадь из её пятерки. Всего 8 забегов. Причем это общий подход, на любое количество лошадей.
А второе решение (7 забегов) пришло, когда а нарисовал на листке положение лошадей после 6-го забега и вычеркнул тех, кто не может претендовать на тройку.
3) Вот это уже сложная задача, решение приличное я не нашел. Она одна стоит больше всех остальных вместе взятых. Даже не утерпел и посмотрел решение Nick_mentat, но к нему у меня много вопросов.
4) Тут 2 варианта. Если разрешено использовать дополнительные ячейки, тогда нужно приводить путем умножение на 10^n к "одной весовой категории", сохранить для каждого числа получившиеся значение, которые уже и сравниваются. При этом, в случае равенства, нужно сравнивать исходные числа (чтобы упорядочить 10 и 100). Этот вариант более быстрый, за счет того, что операция приведение (а она не дешевая) выполняется 1 раз.
Второй вариант — это написание КОМПОРАТОРА, который принимает на вход 2 числа, и сравнивает их. А дальше можно вызывать стандартную функцию сортировки. Это не требует лишней памяти, но требует приведения чисел каждый раз при сравнении.
5) Самый простой способ — это вычислить сумму чисел обоих массивов и произведение чисел (на 0 не умножаем) обоих массивов. Тогда мы получим произведение и сумму искомых чисел. Эти 2 уравнения приводятся к полиному второй степени, и элементарно находятся. Но тут несколько моментов. Первоначально нужно посчитать количество нулей в обоих массивах, если оно разное, то находить произведение смысла нет. С суммой тоже вопросов нет, достаточно взять тип целого, который больше используемых чисел. Например если массивы заполнены 32-битными значениями, то использовать надо 64-битное. С умножением сложнее, умножение пару сотен чисел переполняет даже самые большие типы целого. Значит для умножения нужно использовать числа с плавающей точкой. Да, там будет потеря точности, но старшие биты должны вытянуть (обеспечить) правильное значение.
Насчет задачи 3). Лучшее, что я придумал — это полный перебор, когда первое слово перемешивается со вторым (с сохранением последовательности внутри), для каждого варианта ищем букву из первого слова, которая оказалась равна рядом стоящей букве из второго слова (т.е. эту букву можно выкинуть). Считаем количество таких букв, запоминаем комбинацию, когда их число максимально. Такой алгоритм дает гарантированный результат, но имеет очень плохую алгоритмическую сложность, что-то вроде O(n^m).
Задача с подмножествами. Здесь очевидно, нужно работать с укороченными строками, выбросив те буквы из первого слова, которые не входят во второе, и буквы из второго слова, которых нет в первом. Остатки от 2-х слов уже пытаемся перебором «перемешать» в строку минимальной длины. А уже потом (и это тривиально) добавить выкинутые буквы. В худшем варианте (когда буквы нельзя выкинуть) сложность остается той же, но зато существенно сокращает сложность, когда у обоих слов всего 1 буква общая.
1) Первым делом находим общие буквы в обоих словах. Как это сделать (через хэш, массив или полным поиском) — не принципиально. Общие буквы у них — ACD.
2) Для обоих слов создаем урезанные варианты (оставляем только общие буквы), и при этом запоминаем соответствие. Получаются слова ACAD и ADDCA, и соответствие [3,4,6,7] и [2,5,7,8,10].
3) Создаем массив размером произведения длин сокращенных слов, в нашем случае 4 * 5.
Выбираем слово ACAD проходим по нему с конца (начиная с буквы D). Для каждой такой буквы из первого слова проходим по второму в обратном порядке, и ищем совпадающие буквы. Когда буквы совпали, делаем проход по первому слову в правильном порядке, начиная с буквы справа от текущей (слева от D в нашем примере ничего нет, поэтому ничего не делаем) и смотрим что стоит в массиве для этой буквы и её положения. Находим максимальное значение, прибавляем к нем 1 и сохраняем в массиве в текущем положении и для всех леволежащих, пока не найдем число большее. И того у нас получается тройной цикл (по первому слову-по второму слову-по первому слову).
В чем смысл массива — значение в ячейке x,y говорит нам о максимальной подпоследовательности которая бы начиналась с x-места первом слове, y-места второго слова.
Чтобы было понятно приведу результаты обработки ACAD и ADDCA.
A [3 1 1 1 1]
C [2 2 2 2 0]
A [2 2 1 1 1]
D [1 1 1 0 0]
максимальное значение — 3. Теперь мы имеем в наличии длину искомой последовательности, и букву (положение) в первом слове, с которой она начинается.
4) Используя всё это и массив находим узлы (положение букв в обоих словах) за 1 проход.
В нашем случае A(1,1) C(2,4) A(3,5).
5) Возвращаемся к исходным словам, переводим по таблице соответствия наши узлы.
OPACKADOS и MABUDYDCMAB => A(3,2) C(4,8) A(6,10). После чего разбиваем наши слова на участки (между узлов, узлы не входят ). Берем первый участок первого слова, прибавляем первый участов второго слова и прибавляем сам узел, и так далее…
OP — M — A…
Вычислительная сложность что-то типа между O(n*n+m*n) в случае, если каждая буква встречается не чаще 1 раза и O(n*n*m) в случае, если все буквы одинаковы. Т.е. сложность между квадратичной и кубической.
Ура товарищи!!!
- how long it takes -> how long does it take
- what is the minimum number of races you can find the top 3 -> what is the minimum number of races to find the top 3
- You have make a new string s3 -> You have to make a new string s3
- B contains exactly same numbers -> B contains exactly the same numbers (насчет этого не уверен)
- Find the two elements with minimum time and space complexity -> Find two elements using algorithm with minimum time and space complexity
Спасибо за задачки :)
Почти все не так просты, как кажутся на первый взгляд.
Нетрудно понять, что если бактерий на старте 1, то через две секунды будет их 4.
Значит, для заполнения пробирки от 4-х бактерий надо на 2 секунды меньше, т.е. 60 — 2 = 58.
Впрочем, можно показать это и математически.
Количество бактерий в момент времени “t”, если начинать с 1-й штуки, 0≤t:
1; 2; 4; 8; 16; … 2^t; …
Если взять объем пробирки (кол-во умещающихся бактерий) за "V", и время заполнения в секундах за "t", то имеем:
V = 2^t
Количество бактерий в момент времени “x”, если начинать с 4-х штук, 0≤x:
4; 8; 16; 32; 64; … 2^(x + 2); …
V = 2^(x + 2)
Поскольку объём пробирки один и тот же, то:
2^t = 2^(x + 2)
t = x + 2
Зная, что t = 60 (пробирка от 1-й бактерии заполняется за минуту), имеем:
60 = x + 2
x = 58
Ответ: пробирка с 4-мя бактериями заполнится за 58 секунд.
Есть 25 лошадей. За одну гонку можем пустить не более 5 лошадей.
Что это значит – если в гонке участвовали 5 лошадей, то максимум 3 из них входят в топ-3 (логично, э?), ну а 2 худшие – однозначно не входят.
Разделив лошадей на 5 пятёрок, мы можем за 5 гонок отбраковать из них 5 x 2 = 10 однозначно нетоповых лошадок. Остаётся 15 штук.
По тому же принципу, оставшихся 15 делим на 3 пятёрки, по результатам гонок отбраковываем 3 x 2 = 6 однозначно нетоповых лошадок.
Всего проведено 5 + 3 = 8 гонок, осталось на рассмотрении 25 — 10 — 6 = 9 лошадок.
Делим оставшихся 9 на две группы, одна 5, другая 4 лошадки. Проводим 2 гонки, отбраковывая ещё 2 + 1 = 3 однозначно нетоповых лошадок.
Всего проведено 5 + 3 + 2 = 10 гонок, осталось на рассмотрении 25 — 10 — 6 — 3 = 6 лошадок.
Выбираем из оставшихся 6 лошадок любые 5 и проводим 11-ю по счёту гонку. Ещё 2 однозначно нетоповые лошадки выбывают из рассмотрения.
Всего проведено 5 + 3 + 2 + 1 = 11 гонок, осталось на рассмотрении 25 — 10 — 6 — 3 — 2 = 4 лошадки.
К лучшим 3-м из 11-го забега лошадкам добавляем ту 1-у, которая не участвовала в 11-м забеге, и устраиваем (из получившихся 4-х лошадок) финальный, 12-й, забег.
Лучшие 3-и лошадки по итогам этого, 12-го, забега и будут топ-3.
Итого: на 25 лошадок нужно не менее 12-и забегов. К сожалению, не могу доказать, что данный алгоритм является наилучшим и нет возможности выявить топ-3 за меньшее число забегов, нежели 12.
Ответ: 12 забегов.
Из условий задачи ясно, что 1-я строка будет полностью входить в результирующую строку. Ну а из 2-й строки нужно взять только те символы, которых «не хватает» до объединённого множества символов обеих (с учётом повторяемости, т.е. когда в одной строке есть “r”, в другой – “rrr”, то в конечном результате имеем “rrr”).
Код (C#):
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
static class Solution
{
// для простоты не учитываю регистр символов (но это легко поправить)
static void Main(string[] args)
{
string s1 = "apple";
string s2 = "pear";
// подсчитываем встречаемость символов 1-й строки
Dictionary<char, int> d1 = s1.GroupBy(c => c).ToDictionary(v => v.Key, v => v.Count());
// подсчитываем встречаемость символов 2-й строки, которых "не хватает" в 1-й строке (так, если в 1-й строке есть "r", а во второй "rrr", в d2 попадёт "rr")
Dictionary<char, int> d2 = s2.GroupBy(c => c).Where(v => (!d1.ContainsKey(v.Key)) || (d1[v.Key] < v.Count()))
.ToDictionary(v => v.Key, v => v.Count() - (d1.ContainsKey(v.Key) ? d1[v.Key] : 0));
// буфер результата
StringBuilder result = new StringBuilder(s1);
// проходим по 2-й строке и берём из неё те символы (в порядке их следования), которых не хватает в 1-й строке
for (int i = 0; i < s2.Length; i++)
if ((d2.ContainsKey(s2[i])) && (d2[s2[i]] > 0))
{
result.Append(s2[i]);
d2[s2[i]]--;
}
// вывод результата (если во 2-й строке нет таких символов, которых бы "не хватало" во 1-й строке, то будет выведена в точности 1-я строка)
Console.WriteLine(result.ToString());
}
}
Если в 1-й строке “n” символов, а во 2-й строке “m” символов, то сложность самого алгоритма (без учёта [не]удачности реализации) будет складываться из:
- O(n) на подсчёте встречаемости символов 1-й строки
- O(m) на подсчёте встречаемости символов 2-й строки (предполагаю, что словарь Dictionary<char,int> основан на хэш-таблице со сложностью O(1) в поиске и обновлении элементов)
- O(m) на проходе по 2-й строке для взятия «недостающих» символов
Итого: что-то вроде O(n+m+m).
Похоже на задачу по деревьям. Но поскольку я в них слабоват, то выкрутился другим способом :)
Были бы числа одного порядка (из одинакового количества цифр), то их можно было бы отсортировать по значениям и не париться…
Но коль значения разных порядков, то… можно их привести к одному, наибольшему порядку.
И затем уже сортировать. Правда, при этом числа 100 и 1 могут выстроиться в неправильном порядке относительно друг друга – ведь их «приведённые» значения будут одинаковы и равны, допустим 1000.
Но это на самом деле не проблема – сортируем сразу по двум значениям каждого числа: сначала по «приведённому» значению (1000, 1000), затем по исходному (100, 1), и данные числа таки расположатся правильно.
Код (C#):
using System;
using System.Linq;
static class Solution
{
// вспомогательная структура для сортировки
struct SortingNum
{
// сортировочное значение числа
public int SortValue;
// исходное значение числа
public int OriginalValue;
public SortingNum(int sort_value, int original_value)
{
this.SortValue = sort_value;
this.OriginalValue = original_value;
}
}
static void Main(string[] args)
{
// входной массив чисел
int[] nums = new int[] { 1, 2, 10, 20, 100, 110 };
// находим опорное число - наименьшую степень 10, которую не превосходит ни одно из исходных чисел
int ref_value = 10;
while (ref_value < nums.Max())
ref_value *= 10;
// составляем новый массив из исходных чисел
SortingNum[] sort_nums = new SortingNum[nums.Length];
for (int i = 0; i < nums.Length; i++)
{
int srt_val = nums[i];
// повысить исходное число до порядка опорного числа
while (srt_val < ref_value)
srt_val *= 10;
sort_nums[i] = new SortingNum(srt_val, nums[i]);
}
// сортируем массив чисел, сначала по сортировочному значению числа, затем по исходному
int[] results = sort_nums
.OrderBy(v => v.SortValue).ThenBy(v => v.OriginalValue)
.Select(v => v.OriginalValue).ToArray();
// вывод результатов
Console.WriteLine(string.Join("; ", results.Select(v => v.ToString())));
}
}
Данная реализация не оптимальна, и её можно улучшить. Но даже и так, сложность будет зависеть от сложности используемого алгоритма сортировки.
Надо полагать, оценка O(n*log(n)) будет вполне подходящей.
Что-то нету свежих мыслей. Только тривиальные.
Можно загнать первый массив в хэш-таблицу (множество), получая сложность O(n), где n – размер первого массива.
Затем проходим по второму массиву, и каждое число проверяем на входимость в составленную хэш-таблицу. Если входимости нет – выводим как результат. Сложность O(m), где m – размер второго массива.
Итого: сложность будет как-то так: O(n+m).
Вроде бы и неплохо, но тут у меня совершенно нет закладки на малое количество чисел 2-го массива, не содержащихся в 1-м.
Наверняка, тут должна быть какая-то изощренная мысль, которая от меня пока что ускользает :)
- Вопрос про бактерииСтарая задача. Простейший способ рассуждений: если в 1 пробирке вначале была 1 бактерия, а на 60 секунде — полная пробирка, то в этой же пробирке на 2 секунде — 4 бактерии. Сделаем вторую секунду новым началом отсчёта, и получим правильный ответ — 58.
- Вопрос про лошадейЭту задачу я тоже где-то видел, хотя решение не помнил, пришлось придумывать.
Ответ — 7 заездов.
В первых пяти заездах находим 10 точно-не-призовых-лошади, в 6 заезде из победителей первых 5 находим самую быструю. А дальше, если нарисовать схему, можно заметить, что на 2 и 3 место могут претендовать только 5 лошадей — между ними и разыгрываем 2 и 3 призовые места.
- Задача про строку из подмножеств остается пока что без ответа
- Задача про сравнение чиселИз формулировки условия («Как бы Вы отсортировали целые числа в словарном порядке») не следует наличие у нас полного списка чисел, которые нужно отсортировать (хотя я бы это еще уточнил). Поэтому я бы писал компаратор.
При сравнении нужно число разбить на список символов. Проще всего это сделать в отдельном классе, и поскольку функция split недоступна — нужно будет каждый раз вычислять остаток от деления на 10, сохранять в массив символов. Если число отрицательное — нужно не забыть добавить символ '-' в этот же массив. Поскольку при полной сортировке списка одни и те же числа будут участвовать при сравнении много раз, чтобы не считать каждый раз заново, можно сохранять результаты в хеш-таблицу, хотя ее целесообразность зависит от задачи.
Соответственно, при наличии такого разбиения в компараторе достаточно только перебирать символы массивов с конца до нахождения первого несоответствия.
- Задача про добавленные элементыА вот эта задача мне очень понравилась — мой ответ менялся несколько раз.
От сравнения каждого элемента с каждым я отказался сразу, временная сложность O(n^2) это много. Вот если бы массивы были отсортированы, можно было бы за 1 проход найти нужные элементы… Наверное, их нужно отсортировать. Две сортировки quicksort'ом и один проход по отсортированным элементам — временная сложность O(n*log(n)), памяти используется O(1). Казалось бы, куда лучше?
При сортировке меньшую сложность получить не удастся, значит, для уменьшения сложности от полной сортировки нужно отказаться. Следующая идея появилась при более детальной проверке, что происходит при самой сортировке:
Представим, что прошел первый шаг быстрой сортировки для обоих массивов, и (один и тот же) опорный элемент разделил оба массива на 2 части. Представим, что количество элементов в левой части массива A совпадает с количеством элементов в левой части массива B. Это значит, что все добавочные элементы находятся в правой части массивов , а значит нам левую часть сортировать не нужно!
Пользуясь этой информацией, можно сделать программу, которая после каждого разбиения массивов на 2 части вычисляет, что нам нужно сортировать, а что нет. Если оба добавочных элемента попадают в одну часть массива — значит, вторую сортировать не нужно; если же в разные — ну, тогда придется сортировать обе (хотя поскольку добавочных элемента только два, такое может быть только один раз).
Наибольшее количество сортировок происходит, если при первом же проходе добавочные элементы попадают в разные части массивов, поэтому для оценки сложности рассмотрим этот случай. Для каждого массива первый проход по массиву — это O(n) операций; для каждой половины массивов при первом шаге происходит O(n/2) операций, при втором O(n/4), при третьем O(n/8)… То есть в сумме каждая половина массива подобным образом упорядочивается за O(n) операций, а значит и в сумме будет временная сложность O(n) при сохранении сложности по памяти O(1). Меньше сделать не получится никак, потому что хотя бы один раз, но нужно будет все числа прочитать =)
С этим радостным фактом я пошел читать ответы сообщества. Ничего интересного не нашел, первая задача так и не решена, но для этой задачи многие люди предлагают другое решение — найти сумму и произведение всех чисел для обоих массивов, из этого найти сумму и произведение двух добавочных чисел, а дальше решить квадратное уравнение. Сам способ не очень хорош — для больших чисел или больших массивов произведение сможет храниться только в biginteger, с большой временной сложностью умножения и деления. Хотя, он меня натолкнул на очередное улучшение моего алгоритма:
После каждого разбиения quicksort'ом на две части, проверяем, попадают оба числа в одну и ту же часть или в разные. Если в одну — запускаем этот же алгоритм для этой части (в два раза меньшей). Если в разные — для каждой части ищем сумму в массиве A, сумму в массиве B, и с помощью вычитания находим оба добавочных элемента. Сложность не меняется, также временная сложность O(n) и сложность по памяти O(1), но теперь алгоритм стал гораздо нагляднее.
Скорее всего, это всё еще можно улучшить,Скрытый текстно дальше уже не нужно
На правах рекламыjunior java dev, ищу работу :-)
Задача 2 решения, которые предлагаются мне не до конца ясны, почему то все считают, что устроив 5 забегов для разных наборов лошадей мы каким то образом сможем получить корреляцию между лошадьми из разных забегов, но никто не гарантирует что первая пятерка лошадей, например, не была лучше всех остальных, таким образом на мой взгляд правильное решение такое:
- Проводим забег из 5 лошадей(осталось проверить 20)
- Берём 3-х лучших и добавляем к ним ещё 2-х. И так до тех пор пока не останется тройка лучших.
Всего 11 забегов.
Задача 3 не понимаю что требуется, пример ужасно путает.
Задача 4 думаю нужно привести все числа к одному разряду и дальше отсортировать их, затем сравнить для одинаковых исходные и отсрортивать их.
Задача 5 не хочется повторять решения предложенные выше, поэтому массив B делим пополам и начинаем для каждого элемента из выбранной части массива B выполнят проверку на наличие его в массиве А, как только нашли то удаляем его из массива А и В, если нет печатаем его. Если проверили первую половину полностью то повторяем алгоритм рекурсивно для второй части массива В.
Задача с конями решается быстрее.
Все варианты решения тут уверяют ю что мы можем сказать лошадь 3 из забега 1 точно хуже лошади 1 из забега 2 или 3, потому что нет секундомера, поэтому мы не можем сравнивать лидеров независимы забегов, или я не прав?
Все варианты решения тут уверяют ю что мы можем сказать лошадь 3 из забега 1 точно хуже лошади 1 из забега 2 или 3, потому что нет секундомера, поэтому мы не можем сравнивать лидеров независимы забегов, или я не прав?
Не можем, однозначно не можем, но мы можем их прогнать вместе в 7-м забеге и узнать точно нужные места. Заметьте, что мы оставляем 3 лошади из первой пятерки, 2 лошади из второй, и 1 лошадь из третьей. Всего 6 штук, но одна из них гарантированно лидер.
Прошу ответить, почему некоторые нашли 10/11 забегов минимум, может я чего упустил.
Далее мы выбираем из каждой пятёрки по 1 месту и прогоняем шестым забегом, определяя топ-1. Теперь у нас 14 лошадок и 6 забегов, хорошо (Прошу заметить, господа, 14!).
Каков у нас выбор? Делить группы на 3 и 4 смысла нет, это увеличение кол-ва забегов, поэтому делим по максимуму. 5,5 и 4 лошади на забег, соответственно.
Делаем 7,8 и 9 забеги, определяя (внимание) по 2 самых быстрых лошади на каждую группу (так как мест осталось 2, а остальные снова не помещаются). Итого имеем на 9 забегов группы: 2-2-2 (выкинули 3-3-0, соответственно).
У нас 6 лошадей. Теперь делим на группы по 3 особи. 10 и 11 забеги: находим по самой быстрой лошадке на каждую группу.
12 забегом забираем этих двух лошадок и сравниваем, получая топ-2 и топ-3 места.
Итого остается при беглом рассмотрении всего 9 лошадок (по 3 лошади из 3-х пятёрок). При более подробном рассмотрении, оказывается, что лошадь, которая пришла в прошлый раз первой — лучшая среди всех. И её можно исключать сразу, а далее нужно просто понять кто займет 2 и 3 место. Из оставшихся 8-ми лошадок можно выкинуть ещё 3. Остается как раз 5 для выявления серебра и бронзы.
Заодно, пользуясь случаем, исправлю себя
Шестой забег — это забег лидеров предыдущих пяти пятерок. И те лидеры, которые в шестом забеге прибежали на 4 и 5 месте — уже не попадают в общую лучшую тройку. А значит и лошади из их забегов тоже (они ведь еще хуже).
Объясню на примере: допустим в шестом забеге лошади прибежали в таком порядке:
- Лидер из забега №2.
- Лидер из забега №3.
- Лидер из забега №5
- Лидер из забега №1.
- Лидер из забега №4.
Таким образом ВСЕ лошади из забегов №1 и №4 точно не могут входить в общую лучшую тройку. Ведь лидеры забегов №2, №3 и №5 — гарантированно быстрее.
Day 1: the bacterium is born from a division of his 'mother'.
Day 2: the bacterium divides itself into two bacteria (one of them is a brand new bacterium).
Day 3: the bacterium divides itself into two bacteria again (one of them is a brand new bacterium).
Day 4: the bacterium has already divided itself twice. Now it's ready to die.
Day 5: the bacterium dies.
A unique member of this kind has been collected by scientists. After 8 days, the population is 47. The question is: after how many days will the entire population of bacteria originated by this unique member reach the count of 1,000,000,000,000?
День 1: Рождения бактерии из разделения его прародителя
День 2: Бактерия разделяется надвое
День 3: Бактерия снова разделяется надвое
День 4: Бактерия уже разделилась дважды и готова умереть.
День 5: Бактерия умирает
Ученые пронаблюдали за отдельно взятой бактерией. Через 8 дней численность их
популяции достигла 47 бактерий.
Вопрос: через сколько дней популяция достигнет отметки в 1 000 000 000 000?
Но это Fi_(n+1) + Fi_(n-1), где Fi — это Фибоначчи. Теперь осталось сделать функцию Фибоначчи, которая итерирует n+1 до момента когда есть хотя бы 1*10^12 бактерий, а затем возвращает последний n.
Обещали ответы "на следующей неделе", следующая неделя закончилась. Где ответы?=\
Мне интересно, как 1 задача решается.
В решении последней задачи, наверное, стоило использовать long для сложения квадратов. Конечно, в задачи не указывалось ограничение по максимальному значению, и в примере достаточно малые числа, но квадрат int очень часто выходит за диапазон этого типа.
Также думал складывать числа по разным модулям, чтобы применить теорему об остатках, тоже не дошёл до завершённого решения.
Для начала, предположим, что у нас «лишнее» число одно. Тогда мы можем проxorить все числа из А и В, все совпадающие попадут в этот большой xor дважды и взаимно уничтожатся, останется лишнее число.
unsigned task3(const std::vector<unsigned>& a, const std::vector<unsigned>& b) {
unsigned ans = 0; // Инициализация
for (const auto& v: {a, b}) { // Для обоих массивов
for (const unsigned& e: v) { // Пробегаемся по всем элементам
ans ^= e; // Добавляем к xor'у
}
}
return ans;
}
PS: А еще эта задача часто дается не для двух отдельных массивов, а для одного массива, в котором все числа по парам, а одно (или два) лишнее. Тогда получается еще гарантия того, что найденные числа будут различными, а это чуть легче.
Правильно замечено, что к массиву должна быть применена операция, обладающая свойствами коммутативности и ассоциативности. Например это сложение, умножение, сложение квадратов… и даже xor. Причем нужны минимум 2 операции, чтобы построить систему из 2-х уравнений, поскольку неизвестных у нас тоже 2. Но не любые 2 операции (их комбинация) способны построить решаемую систему. И для xor-а я здесь места не вижу.
Прошу предъявить решение, если оно у вас имеется.
Сначала решение в предположении наличия этой гарантии. Искомые числа не равны, поэтому их xor не равен 0. Поэтому в нем есть хотя бы один ненулевой бит. Значит, наши два числа в этом бите различаются (и этот бит мы умеем находить). Поэтому нам достаточно разделить наш массив на две части: состоящую из чисел, у которых в этом бите 0, и состоящую из чисел, у которых в этом бите 1. Применение к ним решения выше дает наши два числа.
std::pair<unsigned, unsigned> task3(const std::vector<unsigned>& a, const std::vector<unsigned>& b) {
unsigned xor = 0; // xor всех чисел
std::array<unsigned, 32> xors; // массив xor'ов для чисел, у которых в конкретной позиции 1
for (const auto& v: {a, b}) { // Для обоих массивов
for (unsigned e: v) { // Пробегаемся по всем числам
xor ^= e; // Добавляем к xor'у
for (unsigned i = 0; i < 32u; ++i) { // Для каждого разряда
if (e & (1u << i)) { // Если надо
xors[i] ^= e; // Добавляем к соответствующему xor'у
}
}
}
}
for (unsigned i = 0; i < 32u; ++i) { // Пробегаемся по всем разрядам
if (xor & (1u << i)) { // Нашли ненулевой
return{xors[i], xors[i] ^ xor}; // Возвращаем два числа: xor всех, у кого в этом разряде 0 и xor всех остальных
}
}
}
В случае, если наши числа равны, мы дойдем до конца этой функции, не выйдя из нее по if'у в последнем цикле. Не уверен, можно ли дальше обойтись xor'ом единым, но, как минимум, у нас здесь есть уверенность в том, что числа равны, так что можно найти их с использованием сложения (ну, разделить пополам придется). Это уже получается не так красиво, как чисто с xor'ом, и подвержено проблеме переполнения. Но мы хотя бы не требуем того, чтобы сумма квадратов всех чисел умещалась в целый тип — нам достаточно того, что каждое число по отдельности будет меньше, чем 2^31.
В любом случае это один проход.
подумал, что гарантируется, что искомые числа различны (как в уже известной мне задаче про поиск двух непарных чисел в массиве).
Мне кажется, что этот момент не существенен, во-первых потому что он вами указан (т.е. предусмотрен), а во-вторых вы сами же и предложили как его обойти.
Поэтому нам достаточно разделить наш массив на две части: состоящую из чисел, у которых в этом бите 0, и состоящую из чисел, у которых в этом бите 1Не понял идею. Допустим, XOR-ы отличаются на 1, т.е. разница двух чисел только в младшем бите. Но 2 лишних числа могут быть и (12,13), и (201,202). Как восстановить старшие биты?
Код (делается за один проход, чтобы не нужно было сохранять последовательность)Не работает:
int main()
{
std::vector<unsigned> A = { 1, 4, 2, 6, 3 };
std::vector<unsigned> B = { 4, 0, 7, 6, 3, 2, 1 };
auto Z = task3(A, B);
std::cout << Z.first << ',' << Z.second;
return 0;
}
выводит
3435973835,3435973836
Не работает:Ошибка в строке 3: забыта инициализация переменной. Фикс:
std::array<unsigned, 32> xors = {};
Не понял идею. Допустим, XOR-ы отличаются на 1, т.е. разница двух чисел только в младшем бите.Что значит, что разница двух чисел только в младшем бите? Значит, одно из них имеет в этом бите единицу, а другое — ноль. Рассмотрим величину xors[0]: что в нее входит?
В нее входят все те числа из обоих массивов, у которых в нулевом бите единица. То есть, в нее входит ровно одно из наших двух искомых чисел, и еще какие-то другие числа в четном количестве (в четном потому, что они есть и в A, и в B в одинаковом количестве). Учитывая, что a ^ b ^ b = a, получаем, что все числа, кроме искомого, взаимно уничтожаются.
Таким образом, xors[0] равно одному из искомых чисел, а xor — их xor. Значит, xor ^ xors[0] — второе искомое число.
Проверяем «от противного».
Предположим, что в первой пятерке собраны все лучшие лошади. Как это выяснить.
сначала проводится 5 забегов 5-ти пятерок. После чего отбрасываются все 4 и 5 места. (Это точно не 1, 2 и 3 лучшие).
Остается 15 лошадей.
Далее проводится забег 6, 7 и 8 в которых выявляются лидеры среди первых, вторых и третьих мест.
Это нужно еще и потому, что возможен вариант, что в первой тройке разрыв между 1 и 2 местом был столь велик, что туда могли поместиться, например, пара лошадей из другого забега.
Итого выявляются лидеры среди лошадей, занявших 1, 2 и 3 места.
Т.е. 1 место теперь известно точно.
Но 2-я лошадь из 3-го, например, забега, могла быть быстрее, чем 1-я лошадь из второго забега.
Поэтому теперь 4 лошади занявшие 1-е места должны соревноваться с лидером среди забегов «2-х мест» и «3-х мест».
Это значит еще два забега.
Т.е. 9 и 10.
Т.о. чтобы определить три лучших лошади необходимо 10 забегов.
Определение в 7 забегов произвольно исключает вероятность нахождения трех самых быстрых лошадей в одном из первых забегов. Т.о. содержит ошибку.
Если не трудно, то приведите контрпример — вариант, когда алгоритм дает не правильный результат. Уверяю, что вы этот контрпример не сможете подобрать.
В соответствии с алгоритмом из поста, мы проводим 5 забегов и получаем следующую картину:
1 2 3
6 7 8
11 12 13
16 17 18
21 22 23
Вычеркнуты те лошади, которые в тройку лучших точно не попадут, а жирным выделены те, что точно попадут.
Дальше мы проводим 6 забег: пять лучших лошадей (1 6 11 16 21). Из него точно понятно, что лошадь 1 в ответ попадет (поскольку она лучше всех). Также понятно, что лошади 16 и 21 в ответ не попадут (поскольку есть три лошади, которых они хуже: 1, 6 и 11). Также в ответ не попадут лошади 12 и 13 (поскольку они хуже, чем 11 лошадь, судя по первым пяти забегам, а 11 лошадь хуже, чем 1 и 6). И еще в ответ не попадет лошадь 8 (поскольку она хуже, чем 6 и 7, а 6 хуже, чем 1).
То есть, мы получаем следующую картину:
1 2 3
6 7
11
То есть, нам осталось из лошадей 2, 3, 6, 7 и 11 выбрать тех, кто попадет на 2 и 3 места. Очевидно, это можно сделать за один забег (7), поставив в него всех этих лошадей и взяв двоих лучших.
Это то, как работает алгоритм из статьи в случае, если все лучшие лошади попадают в первый забег. Вы же провели забеги 6, 7 и 8 по-другому, из-за чего вам и потребовалось больше забегов. Это не доказывает, что нельзя сделать за 7.
Выпуск#5: ITренировка — актуальные вопросы и задачи от ведущих компаний