Чирак 2022 D

  • Фернандо Пеня
  • Давид Карденас
  • Реджина Еспиноса
  • Мануел Кота
  • Сантяго Перейра
  • Едуардо Мартинес
  • Хесус Мургия

Луни и чадъри

ОБЩ ПРЕГЛЕД:

Проблемът включва получаване на низ от Cs и Js. За всеки срещнат „CJ“ има стойност, която трябва да бъде платена, както и различна стойност за всеки „JC“ в низа. Освен това низът съдържа въпросителни знаци (?), където нашата програма трябва да реши коя буква, C или J, трябва да бъде поставена, за да може крайната цена на низа да бъде най-малката сума.

Входът съдържа броя на низовете, които трябва да бъдат оценени на първия ред. Всички следващи редове са във формата „X Y ‹низ›“, където X е цената „CJ“, а Y е цената „JC“. Алгоритъмът анализира всеки знак в низа, сравнявайки го с предишния знак, освен ако текущият знак не е въпросителен знак (?). В този случай започва със сравняване на предишния и следващия знак.

Решението също така взема предвид случаите за тестов набор #3, в които цените X и/или Y могат да бъдат по-малки от нула. В този случай „CJ“ и/или „JC“ могат да бъдат предпочитани вместо избягвани.

КОНТЕКСТ:

Ситуацията включва художник Коди-Джамал, чието абстрактно изкуство има полумесеци и чадъри, които могат да бъдат объркани съответно с Cs и Js. Всяка творба на Коди-Джамал може да бъде изразена като низ от Cs и Js. Например „CJCCJJC“. В случай че в поредицата се появи „CJ“, трябва да се плати възнаграждение за авторски права от X пари. Когато се появи „JC“, цената на възнаграждението е Y. Освен това някои низове са непълни и имат въпросителни знаци (?), които символизират празни интервали, например „CJ?CC?“.

Проблемът се състои в това да се преброи колко пари трябва да се платят за даден низ и в случай, че има празни места (?), трябва да се вземе решение кой символ да се постави в него, за да бъде крайната цена най-малка възможна сума.

РЕШЕНИЕ:

Като цяло, решението трябва да прочете всеки входен ред и да го изпрати на функция, която проверява всеки знак. Чрез сравняване на всеки знак с предишния, той може да забележи дали последователността някога се променя от C на J или J на ​​C. В случай че анализираният знак е въпросителен знак (?), той взема решение кой е най-подходящ за това пространство и го изпраща в цикъла като „предишен знак“. Резултатът от този алчен алгоритъм се записва в изходен файл като „Case ##: ‹price›“.

Решение на алчен алгоритъм:

Когато програмата за първи път получи вход, тя извиква итерационната функция. За първия знак няма ефект, тъй като няма предишен знак, с който да го сравним. Когато стигне до втория знак, той се сравнява с предишния. Ако са еднакви, значи всичко е наред и цикълът продължава. Ако са различни, тогава се проверява дали предишният е C. Ако случаят е такъв, тогава знаем, че имаме работа с „CJ“, така че добавя единица към брояч за този модел. Другият случай, ако е J, ще добави единица към брояч за JC модел.

Това би било достатъчно, ако няма липсващи интервали. Ако програмата срещне въпросителен знак (?), тя първо сравнява предишния знак със следващия. Ако този отзад и този отпред са абсолютно еднакви и никоя от цените на шаблона не е по-ниска от нула, тогава се избягва всяка промяна на последователността и се поставя същият знак. Например, ако има „C?C“ и както моделът „CJ“, така и „JC“ всъщност струват пари на художника, тогава най-удобното решение е тези знаци да бъдат „CCC“. Ако обаче поне една от цените на модела е отрицателна (художникът получава заплащане), тогава може да е добре да нарушите последователността. Все пак това би било добре само ако сумата на цените е под нулата.

Който и да е моделът („C?C“ или „J?J“), прекъсването на последователността ще получи както „CJ“, така и „JC“ последователност. Следователно, с този ход ще се покачат и двата брояча по един. Така че, ако цена X плюс цена Y завърши с число под нула, това означава, че всъщност искаме това да се случи. Въпреки това, дори ако една от цените, X или Y, е под нулата, сумата не е задължително да бъде. Ето защо решението трябва да провери дали тази сума е отрицателна или не, преди да вземе решение.

Другата възможност за среща на въпросителен знак (?) в средата на последователността е много по-лесна за справяне: когато предишният и следващите знаци са различни. В този случай ще настъпи промяна в последователността, независимо какво се случва. Вземете например последователността "C?J". Ако постави C, ще има модел CJ. Но това също ще се случи, ако постави J. Това би повлияло само колко скоро в последователността ще се появи моделът, но броячът все пак ще се повиши. Така че може просто да направи това и да премине към следващия знак.

Изключенията се случват в началото и в края на входния ред. В края е по-просто, така че нека започнем от там (иронично). В този случай няма „следващ знак“, което означава, че програмата ще има контрол върху решението да създаде модел или не. Като провери предишния символ, той ще знае дали потенциалният модел е „CJ“ или „JC“. Ако този модел има отрицателна цена, тогава той предпочита промяната, тъй като означава печелене на пари. Въпреки това, ако цената е положителна сума, тогава тя избягва поставянето на същия знак като предишния.

Сега, ако има въпросителен знак (?) в началото, анализът става малко сложен, но все пак управляем. Тук няма предишен знак, така че не знае дали ще има модел или не. Разбира се, може да погледне напред към следващия герой и да вземе решение с него. Но какво се случва, когато следващият знак също е въпросителен знак? Решението е просто, въпреки че анализът може да бъде малко досаден.

Ако въвеждането започва с един или повече въпросителни знаци (?), тогава програмата ще премине към специална функция, която първо преброява колко празни места има преди следващото C или J. Ако и двете цени на модела са положителни суми, програмата избягва създаване на каквито и да било шаблони и просто счита тези интервали за запълнени със същия знак, който среща за първи път след въпросителните знаци (?). Ако обаче една от цените е отрицателна и тяхната сума също е отрицателна, тогава тя може да използва редуване през празните полета, стига общата сума да е отрицателна в крайна сметка. В зависимост от това дали броят на празните полета е четен или нечетен, коя от цените е отрицателна и кой символ е първият, който се среща, може да се изчисли подходящият брой модели, както може да се види в Таблица 1.

Друг специален случай е, когато една цена е отрицателна, но сумата все още е положителна. В този случай може да има възможност да се постави един шаблон в този начален низ от празни интервали, но само ако първият срещнат знак е подходящият. Да предположим например, че входът е „???C…“ и че X = 3 и Y = -2. В този случай можем да направим така, че да има един модел „JC“. Но ако вместо това цените бяха X = -2 и Y = 3, тогава единственият начин да поставите JC модел би бил да създадете и CJ модел. Общата цена на двата модела в крайна сметка е положителна и затова програмата трябва да я избягва.

И накрая, може да дойде случай, когато всички знаци във входа са въпросителни (?).

Таблица 1 — Специални случаи за начален низ от въпросителни знаци (?): таблицата показва всички случаи, които изискват увеличаване на броя CJ (граф X) или JC (граф Y) модели. Тук N е броят на въпросителни знаци (?), а срещнатият знак се отнася до първия символ, намерен след споменатия начален низ.

Последната възможност е целият входен низ да е само въпросителни (?). Ако и X, и Y са над нулата, тогава програмата може да избегне всяка последователност, като постави един и същ знак във всички интервали. Ако поне една цена е под нулата и сумата от двете цени също е отрицателна, тогава се предпочита да се създадат възможно най-много модели по двойки, за да се реализира печалба. В зависимост от това дали броят на празните интервали е четен или нечетен, съответните преброявания за шаблони CJ и JC ще се увеличат и те ще бъдат отпечатани в изходния файл.

Решение за динамично програмиране:

За втория набор от тестове считаме, че „CJ“ или „JC“ също може да бъде отрицателно число и следователно изисква много по-различен подход за решаване на проблема, динамичен подход.

Динамичният алгоритъм за този проблем използва подход отдолу нагоре за решаването му. Започва с инициализиране на разходите за използване на намаляваща луна или затворен чадър на всяка позиция в низа до безкрайност (в зависимост от езика за програмиране това може да е просто „много голямо“ число). Ако първият знак в низа е „J“, цената за използване на затворен чадър в тази позиция е зададена на 0, а ако е „C“, цената за използване на намаляваща луна в тази позиция е зададена на 0.

След това алгоритъмът преминава през низа, започвайки от втория знак. На всяка позиция той актуализира цената за използване на намаляваща луна или затворен чадър въз основа на героя в тази позиция, както и разходите за използване на намаляваща луна или затворен чадър на предишната позиция. Ако символът е „C“, цената за използване на намаляваща луна в тази позиция е зададена на минимума от цената за използване на намаляваща луна в предишната позиция и цената за използване на затворен чадър в предишната позиция плюс разходите за използване на затворен чадър.

Ако символът е „J“, цената за използване на затворен чадър в тази позиция се задава на минимума от цената за използване на затворен чадър в предишната позиция и цената за използване на намаляваща луна в предишната позиция плюс разходите за използване на намаляваща луна. Ако знакът е „?“, разходите за използване на намаляваща луна и разходите за използване на затворен чадър в тази позиция са зададени на минимума от разходите за използване на намаляваща луна или затворен чадър в предишната позиция.

След итерация през целия низ, алгоритъмът връща минималната цена на използване на намаляваща луна или затворен чадър на последната позиция в низа.

АЛТЕРНАТИВНИ РЕШЕНИЯ:

Основен недостатък на използването на алчния алгоритъм при работа с третия тестов набор е, че може просто да се почувства като хубав начин за вмъкване на изрази „if-else“. Като създаване на дърво с толкова клони, колкото възможности има. В този случай обаче възможностите са ограничени, тъй като входът и неговата дължина не засягат различните случаи, което прави проблема и кода все още достатъчно управляеми.

Друг начин за внедряване на решението за динамично програмиране, което беше използвано за третия тестов случай, е подходът „отгоре надолу“, който се справя с проблема по по-рекурсивен начин. Междувременно подходът „отдолу нагоре“, използван тук, прилича повече на решение на алчен алгоритъм. И двата варианта бяха разгледани, но решихме да използваме втория за простота.

Reversort Engineering

ОБЩ ПРЕГЛЕД:

Проблемът включва получаване на дължината на низ от последователни цели числа (започващи от 1) и дадена цена. Резултатът трябва да бъде заплетеният низ, чиято цена за сортирането му с помощта на функцията „reversort“ е цената, дадена като параметър.

Входът съдържа първо броя на случаите за анализ (или броя на заплетените низове, които трябва да изведем), последван от дължина N и цена C на всеки ред, по един ред за всеки случай. Алгоритъмът анализира дали низ, чиято цена е дадена, е възможен, като изчислява обхвата на възможните разходи. Ако низът е възможен, той започва с низа с минимална цена и го променя, като създава низ, чиято цена е с единица повече от преди. Той повтаря този процес толкова пъти, колкото е необходимо, за да може мутиралият низ да има желаната цена.

КОНТЕКСТ:

Има вече работеща функция, наречена „reversort“, чиято цел е да сортира заплетен низ, обръщайки секции от споменатия списък със знаци. Той проверява стойността на първия индекс, след което гледа напред за най-ниската стойност. Когато го намира, той взема цялата подсекция от текущия индекс до индекса с тази най-ниска стойност и обръща цялата подпоследователност. След това продължава със следващия елемент и повтаря процеса. Ако някога установи, че няма по-ниска стойност напред, това означава, че текущата стойност вече е на правилното си място и преминава към следващата.

За този проблем обаче се оказва, че обръщането на подсекция от низа има цена, равна на броя елементи, които са били обърнати. Например, ако сме били при индекс 𝑖 и най-ниската стойност напред е намерена при индекс 𝑗, това означава, че броят на обърнатите елементи е 𝑗−𝑖+1. Допълнителният се добавя, защото включваме и двата края на този подраздел, който обърнахме.

Когато даден елемент вече е на правилното си място и няма действително обръщане, цената всъщност е 1. Можем да го приемем, сякаш е обърнал подсекция на един елемент (което по същество означава, че нищо не се е случило, но все още има цена).

РЕШЕНИЕ:

Като се има предвид този последен бит информация, най-добрият сценарий за низ би бил той вече да е подреден. Например, ако имаме 1 2 3 4 с 𝑁=4, цената му ще бъде 𝐶=3, тъй като последният елемент не е анализиран. Не е необходимо, тъй като вече ще бъде подредено, докато функцията стигне до него. И така, като погледнем това, можем да видим, че най-ниската възможна цена за низ от 𝑁 символи всъщност е:

𝐶𝑚𝑖𝑛=𝑁−1

Сега, максималната възможна цена е, когато при всяка итерация трябва да обърнем колкото е възможно повече елементи. В миналия пример, за 𝑁=4, това би било 2 4 3 1, тъй като при всяка итерация ще обръща всички елементи отляво на текущия индекс.

Анализирайки това, можем да видим, че събираме 2+3+…+𝑁. Следователно максималната възможна цена се дава от израза на Гаус за сумата от всички цели числа до дадено число N, но като се извади 1, тъй като всъщност спираме, когато цената е 2:

𝐶𝑚𝑎𝑥=𝑁⋅(𝑁+1)2−1

С това вече можем да кажем дали дадена цена за дадена дължина на низа е възможна или не. Ако не е, ние дори не губим време да изчисляваме каквото и да било. Ако обаче цената е в диапазона [𝐶𝑚𝑖𝑛, 𝐶𝑚𝑎𝑥], тогава трябва да намерим едно от многото възможни решения. За да го направим, можем просто да започнем със списъка с най-ниски разходи и да започнем да го променяме, като добавим 1 към цената му. Трябва само да повторим процеса 𝐶 − 𝐶𝑚𝑖𝑛 пъти.

Начинът да направите това е да започнете, като вземете втория до последния елемент от дясната страна на низа и го натиснете докрай надясно, което го прави последния елемент. След това третият предпоследен, след това четвъртият предпоследен и т.н. Например, да кажем, че имаме 𝑁=4. Низът с най-ниска възможна цена би бил 1 2 3 4. Като изпратим тези елементи надясно и анализираме цената за всеки случай, можем да видим, че това е правилната мутация:

Но какво се случва сега? Максималната цена за 𝑁=4 би била 𝐶𝑚𝑎𝑥= 9, според нашето уравнение. Можем да видим, че това, което се случва, е, че искаме бавно да променим списъка, докато стане максимално възможният списък с разходи: 2 4 3 1.

Ако отделим малко време, за да анализираме този низ, можем да видим, че той започва възходящ списък от четни числа (2, 4, …) и след това завършва с низходящ списък от нечетни числа (…, 3, 1). И така, в последната ни итерация преди, когато стигнахме до 𝐶=6, последният ни елемент вече беше 1. Вече имахме един елемент, какъвто бихме искали, за да получим в крайна сметка списъка с максимални разходи (дори ако целта ни е да вземете низ много преди това). В такъв случай можем да спестим този елемент и да започнем отново нашия алгоритъм, но го прилагаме само към левия подсписък. Следвайки това, което имахме преди, сега щяхме да имаме:

Сега можем да видим, че отляво имаме 2 в правилната му позиция, за да стане в крайна сметка списъкът 𝐶𝑚𝑎𝑥. Така че сега можем да запазим този елемент и да започнем отново с оставащия подниз. В този случай приключихме, защото следващият мутирал списък ще бъде 2 4 3 1, което е нашият списък с максимални разходи от 𝐶𝑚𝑎𝑥=9.

Като следваме този модел и спестяваме елементите, които заемат позиция в края на списъка, като редуваме кой край да проверяваме след всяко спестяване, можем да получим всеки низ, за ​​който желаната цена е възможна.

Важно е да се отбележи, че може да има и други решения за дадена цена. Това обаче е ефективен начин за получаване на един възможен отговор.

АЛТЕРНАТИВНИ РЕШЕНИЯ:

Повече от алтернативно решение, открихме, че внедряването на този алгоритъм може драстично да се промени от един програмен език на друг. Затова препоръчваме да опитате да разрешите този проблем на език, който напълно поддържа цикли и управление на списъци/масиви.

По-специално, Haskell може да бъде обезпокоителен, като се има предвид колко трябва да зависи от рекурсията и как не позволява мутация на каквито и да било променливи.

Средно сортиране

ОБЩ ПРЕГЛЕД:

Проблемът някак работи като игра между две програми: медианен сортировач и съдия. Съдията има поредица от 𝑁 числа, които вървят от 1 до 𝑁 в произволен ред. Средният сортировач знае само дължината 𝑁 на последователността и трябва да изведе реда, като задава въпроси на съдията. Въпросът обаче може да бъде само три числа, като се пита кое от тях е между другите две в скритата последователност, която съдията има.

Така че трябва да сортираме нашия низ, като знаем само медианата на три числа наведнъж. Освен това съдията предава и параметър 𝑄, който е броят на въпросите, които медианният сортировач може да направи.

КОНТЕКСТ:

Програмата съдия генерира списък от 𝑁 цели числа, всички стойности от 1 до 𝑁, в произволен ред. Програмата за сортиране на медиани трябва да комуникира с програмата съдия, изпращайки три от тези стойности и получавайки коя е средната стойност сред другите две. Да предположим например, че съдийската програма е генерирала низа 2 4 3 1. Средният сортировач би знаел, че 𝑁=4, но това е всичко.

За да може сортировачът да се опита да разбере низа, той трябва да започне да иска медианата на различни групи от три числа. Следвайки предишния пример, той може да изпрати 1 2 4 на съдията, на което съдията ще отговори 4, тъй като в тази последователност 4 идва между 1 и 2.

Това би означавало, че списъкът може да бъде 1 4 2 или 2 4 1. Тъй като е невъзможно да се разбере коя е правилната ориентация, съдийската програма ще приеме всяко решение за валидно.

Средният сортировач може да зададе само 𝑄 брой въпроси на съдията, за да заключи коя е последователността от цели числа.

РЕШЕНИЕ:

Най-добрият подход всъщност ни позволява да игнорираме 𝑄, тъй като броят на необходимите въпроси, които трябва да зададете на съдията, няма да надхвърли максималния разрешен брой.

Можем да започнем, като имаме собствен списък или последователност, в който можем да вмъкнем всяка стойност, когато намерим къде трябва да бъде поставена. Първоначално можем да имаме този списък като [1,2]. С това можем да започнем, като започнем от индекс 3, тъй като това е следващото цяло число, което трябва да вмъкнем в нашия списък. Затова изпращаме тези три числа на съдията, задавайки му 1 2 3. Да предположим, че имаме 3 6 1 5 2 4 като скрит списък от 𝑁=6. В този случай съдията ще отговори 1.

Тъй като текущият ни списък беше [1,2] и те просто ни казаха, че 1 е в средата, тогава можем просто да поставим 3 в самото начало, което води до [3,1,2]. След това изпращаме две цели числа от този списък заедно със следващия индексен номер, който ще бъде 4. Но кои числа трябва да изпратим от нашия списък. Това е мястото, където идва основната идея на този алгоритъм.

Да предположим, че имаме списък от 𝑁 елементи. Това би означавало, че имаме индекси от 0 до (𝑁−1). Можем да изчислим кои индекси са на приблизително една трета и две трети от дължината на списъка:

0−𝑡ℎ ………… 13(𝑖) ………… 23(𝑗) ………… (𝑁−1)−𝑡ℎ

Можем да наречем тези индекси 𝑖 за една трета от дължината и 𝑗 за двете трети от дължината. Можем да ги изчислим по следния начин:

𝑖 = 𝑐𝑒𝑖𝑙(𝑁3)−1

𝑗 = (2 ⋅𝑐𝑒𝑖𝑙(𝑁3))−1

Тук 𝑐𝑒𝑖𝑙() означава „закръгляване“ на делението. По този начин, дори за списък 𝑁=2, ще получим 𝑖=0, 𝑗=1. С тези два елемента можем да зададем правилния въпрос на съдията. Изпратеният въпрос ще бъде (𝑖−𝑡ℎ, 𝑗−𝑡ℎ, 𝑖𝑛𝑑𝑒𝑥), като индексът е следващото число за вмъкване. По този начин има три основни възможности.

Първо, да предположим, че програмата отговори, че 𝑖𝑛𝑑𝑒𝑥 е средното число. В този случай вече знаем, че трябва да вмъкнем това ново число между индексите 𝑖−𝑡ℎ и 𝑗−𝑡ℎ.

… 13(𝑖) ………… 23(𝑗) …

Тук има само два случая. Първо, ако 𝑗−𝑖=1, това означава, че елементите 𝑖−𝑡ℎ и 𝑗−𝑡ℎ са точно един до друг. Това означава, че нашата 𝑖𝑛𝑑𝑒𝑥 стойност може просто да бъде вмъкната точно там между тези два индекса.

Ако, от друга страна, има един или повече елементи между тях, тогава разликата ще бъде по-голяма от единица. След това просто изрязваме подраздела на нашия списък от 𝑖 до 𝑗 (включително) и започваме отново да получаваме нови стойности за 𝑖 и 𝑗. Рекурсивно може да продължи да го прави, докато мястото за вмъкване на новия номер стане единствената възможност.

Сега, какво ще стане, ако в някоя от тези итерации средната стойност излезе като 𝑖−𝑡ℎ елемент? Тогава това би означавало, че новата стойност трябва да бъде вмъкната някъде между началото на списъка и елемента 𝑖−𝑡ℎ.

0 ………… 13(𝑖) …

Отново могат да възникнат два случая. Първо, ако 𝑖 = 0, тогава 𝑖 е в самото начало на последователността. Това означава, че нашата нова стойност просто трябва да бъде поставена преди всички елементи и това е всичко.

Ако обаче 𝑖 не е първият индекс, тогава има един или повече елемента отляво и позицията на нашия номер може да бъде навсякъде в тази последователност. В този случай просто вземаме подраздела, който преминава от индекс 0 до елемента 𝑖−𝑡ℎ (включително) и повтаряме процеса.

И накрая, но по подобен начин, ако дойде отговорът, че елементът 𝑗−𝑡ℎ е средният, тогава това означава, че нашето число трябва да бъде поставено някъде от елемента 𝑗−𝑡ℎ до края на низа.

… 23(𝑗) ………… (𝑁−1)−𝑡ℎ

Лесният случай тук е, ако 𝑗 = 𝑁−1. Това означава, че 𝑗 е последният елемент в последователността, което означава, че нашият номер може просто да бъде добавен в края на списъка.

Ако това не е така, тогава има един или повече елементи отдясно на елемента 𝑗−𝑡ℎ и трябва да повторим процеса, изпращайки подсекцията от индекса 𝑗−𝑡ℎ до края на списъка (включително).

Целият този процес трябва да се повтори, докато последният номер, вмъкнат в списъка, е 𝑁. Това означава, че решението е готово с всички числа от 1 до 𝑁 в правилния им ред в списъка.

Освен това съдията първоначално ще предаде аргумент 𝑇, който показва колко пъти програмата трябва да се изпълни, получавайки стойност за 𝑁 и опитвайки се да намери списъка. Както казахме преди, всеки път ir също ще получи 𝑄 параметър, указващ максималния брой позволени въпроси, въпреки че с този алгоритъм броят на зададените въпроси никога не е толкова голям, колкото стойностите на 𝑄, които се предават.

Подходът, който да се използва за този проблем, трябва да бъде „троично търсене“, ако броят на взаимодействията с програмата съдия трябва да бъде 𝑂(log3𝑁). С това и трите представени теста работят в рамките на границите и остават в рамките на желания брой взаимодействия 𝑄.

АЛТЕРНАТИВНИ РЕШЕНИЯ:

Имаше други алгоритми, които се опитваха да генерират пермутации на елементите от текущия списък на всяка стъпка. Това, за да поиска всяка възможна триада от числа. Съхранявайки ги в паметта, то ще търси модела, който прави всичко вярно. Би било нещо като генериране на куп парчета от пъзел и след това да се опитате да ги съберете заедно. Това обаче определено би имало опасност да достигне до редица 𝑄 въпроси.

Партньорство за родителство

ОБЩ ПРЕГЛЕД:

Проблемът включва получаване на списък с дейности, всяка с начално и крайно време в минути, и делегирането им на двама партньори, 𝐶 и 𝐽. Това е всичко, за да се предотврати на който и да е партньор да бъдат делегирани две дейности, които се припокриват. Въпреки това, ако партньорът трябва да извърши две дейности, които се припокриват, тогава той няма да може да завърши списъка с дейности. В такъв случай програмата трябва да посочи, че това не е възможно.

КОНТЕКСТ:

На програмата първо се дава параметър 𝑇, който показва колко пъти трябва да се изпълни програмата, като се анализира списък от дейности и се вижда дали те могат да бъдат успешно делегирани на двама партньори, 𝐶 и 𝐽. За всеки път, когато се изпълнява, изходът ще бъде низ от знаци „C“ и „J“, които показват реда, в който всеки партньор е ангажиран със задача. Съществува също така възможността три дейности да са се припокрили и така резултатът да бъде „НЕВЪЗМОЖЕН“, тъй като те не биха могли да изпълнят всички дейности.

За всеки случай програмата ще получи първо параметър 𝑁, указващ броя дейности, които трябва да се опитат да делегират. След това следват 𝑁 редове, всеки с два елемента: начален час 𝑆𝑖 и краен час 𝐸𝑖, указващи началото и края на дадена дейност.

Важно е да се отбележи, че списъкът с дейности не е непременно в какъвто и да е ред. Така че сортирането му по някакъв начин трябва да бъде първата задача преди делегирането.

РЕШЕНИЕ:

Както беше посочено преди, първо трябва да сортираме списъка с дейности. Най-добрият подход е да го сортирате във възходящ ред по началния час 𝑆𝑖 на всяка дейност. По този начин можем да започнем да ги анализираме от първите елементи в списъка.

След като се сортира, можем да започнем да делегираме. Ако се стигне до ситуация, в която не можем да делегираме дейност на нито една от двете страни, тогава прекъсваме цикъла и извеждаме, че това е „НЕВЪЗМОЖНО“. За да започнем делегирането, можем да установим един от партньорите като приоритет. В този случай нека поставим партньора 𝐶 като приоритет.

Получаваме първата дейност в списъка и виждаме дали 𝐶 е заето. Тъй като това е първата дейност, която ще бъде делегирана, тогава знаем, че той ще бъде наличен, така че можем да настроим „предишната“ дейност на 𝐶 да бъде тази, съхранявайки нейните 𝑆 и 𝐸 стойности.

Когато преминем към следващата дейност в списъка, отново проверяваме първо с нашия приоритетен партньор 𝐶. За да проверим дали е наличен, сравняваме началния час 𝑆 на нашата дейност с крайния час 𝐸𝐶 на дейността на партньора 𝐶. Ако се окаже, че 𝐸𝐶 ‹ 𝑆, това означава, че 𝐶 ще е завършил предишната задача до момента, в който започне следващата. Следователно можем да го делегираме на 𝐶.

Ако обаче 𝑆 ‹ 𝐸𝐶, тогава 𝐶 все още ще бъде зает, когато започне тази следваща дейност. И тогава се обръщаме към 𝐽. Въпреки това не можем просто да го делегираме на 𝐽, защото все още трябва да видим дали е наличен.

За да направим това, правим същите сравнения между началния час 𝑆 на дейността, която искаме да делегираме, и крайния час 𝐸𝐽 на предишната дейност на 𝐽. Ако това е първата дейност, която ще бъде делегирана на 𝐽, тогава няма да има проблем, но във всички останали случаи ще трябва да видим дали 𝐽 не е заето.

Отново, ако 𝐸𝐽 ‹ 𝑆, тогава 𝐽 не е зает, можем да го делегираме и можем да преминем към следващата дейност в списъка. Ако обаче 𝑆 ‹ 𝐸𝐽, това означава, че 𝐽 все още е зает с предишната задача до момента, в който тази задача започне. Ако възникне тази ситуация, вече видяхме, че и 𝐶, и 𝐽 са заети. Така че тази дейност не може да бъде делегирана на нито едно от двете. След това програмата спира да анализира дейностите и извежда „НЕВЪЗМОЖНО“.

Всеки път, когато дадена дейност се делегира на който и да е партньор, низ може да се актуализира, добавяйки като следваща буква 𝐶 или 𝐽, указвайки реда, в който е делегирана всяка дейност.

Тъй като може да има множество начини за делегиране на дейностите чрез промяна на приоритета на 𝐽, резултатът може да бъде един от многото възможни отговори. Все пак това е валиден начин за разпределяне на задачите между двамата партньори.

АЛТЕРНАТИВНИ РЕШЕНИЯ:

Ако дейностите са дадени в ред от самото начало, известно време на изпълнение може да бъде спестено чрез делегиране на задачата веднага след получаването й (или виждане, че не може да бъде делегирана). По този начин низът от знаци „C“ и „J“ се актуализира само когато следващата дейност се дава като вход.

РЕШАВАНЕ НА РАЗЛИЧНИ ЕЗИЦИ:

Първоначално екипът беше разделен на подекипи, всеки от които се фокусира върху изучаването на конкретен език за програмиране, така че всички да можем да използваме тези прозрения за решаване на проблемите. Имахме срещи, за да обсъдим грешките и да си помогнем взаимно по отношение на различни пречки. Използвахме наличните канали за комуникация, за да се информираме взаимно за напредъка си. Бавно ставаше все по-забележимо, че всеки език има своите усложнения, както и своите предимства.

Groovy, имащ синтаксис, подобен на Java, направи алгоритмите лесни за внедряване с помощта на списъци. Като се имат предвид всички включени функции, които вече има, като сортиране, тяхното манипулиране беше по-лесно, отколкото в други езици, въпреки че това не беше единственият език от тези, които използвахме, който имаше тези функции. Особена характеристика на този език обаче беше използването на променлива от тип double за представяне на положителната безкрайност. Това улесни програмирането на алгоритми като тези, използвани в Median Sort или Reversort Engineering.

В Ruby, обратното на Groovy, целочисленото деление се открива автоматично. Други основни разлики включват, че всичко се третира като обект, дори числата. Можем да се възползваме от този факт, като създаваме цикли, като използваме метод самото число. Това е позволено да се направи и с масиви, които бяха от изключително значение за алгоритмите, които внедрихме. Този език може да бъде строг по отношение на формата за именуване на променливи и функции, тъй като не е разрешено променливата да започва с главна буква. Това се използва за разграничаване между променливи и имена на класове.

Машинописът изисква малка корекция в нашия манталитет поради сходството му с Javascript и други структурни езици. Основната разлика беше неговата строгост с типовете променливи и фактът, че трябваше да бъде преведен на Javascript, преди програмата да може да бъде компилирана и стартирана. Ето защо този език също изисква използването на локален сървър, използването на Node и т.н. Подобно на Groovy, Typescript също има променлива, която може да се използва за представяне на положителната безкрайност. Основните трудности, които срещнахме с този език, бяха управлението на въвеждането, независимо дали е обикновен текст или като цяло. Стана по-лошо, когато се работи с интерактивни програми, като например с решението за средно сортиране.

И накрая, Haskell представлява най-голямото предизвикателство, тъй като е напълно функционален език. Променливите не могат да бъдат мутирани, цикличните структури не съществуват в синтаксиса (позволяват само рекурсия) и той е най-стриктният по отношение на типовете променливи от всеки от другите три езика. Той представлява стандартния вход и изход с „обвивка“ (монади) около стойността, която функцията връща. Основният урок, който научихме, използвайки този език, дойде с постоянната практика на рекурсия, което ни позволи да внедрим алгоритмите възможно най-добре.

Въпреки че всеки подекип имаше специален фокус върху един език, всеки път, когато целият екип се събираше, всички можехме да се възползваме от това, което всеки използваше и беше научил. Всяка среща донесе по-широка картина на всеки проблем, тъй като вече можехме да обмислим други възможни решения. Понякога дори осъзнавахме, че инструментите или включените функции, които съотборникът използва на един език, са налични и на нашия собствен език, което прави процеса много по-лесен.