Это статья об основах программирования на Go. На канале я рассказываю об опыте перехода в IT с нуля, структурирую информацию и делюсь мнением.
Хой! Джедаи и Амазонки!
Прошёл экзамен от Академии Тинькофф для поступления на бесплатное обучение с перспективой стажировки и трудоустройства. Решил 3 задачи и ещё одну частично. Всего было шесть задач. Результаты будут 18 февраля - станет известно, зачислили меня на курс, или нет.
В прошлом посте рассказывал, как готовился к экзамену. В этой статье покажу все задачи контеста Тинькофф февраля 2023 г., затем - код задач, которые смог решить и поделюсь впечатлениями. Go!
Задачи Академии Тинькофф
На экзамен давалось три часа. Задачи разной сложности: первые три попроще, следующие три посложнее.
Также давалась анкета, которая влияет на итоговый результат:
Задача 1
Ограничения
- Время: 1 секунда
- Память: 256 МБ
Условия
Каждый раз, возвращаясь с работы, Василий вынужден отстоять большую очередь, чтобы сесть в маршрутку.
В очереди перед Василием находится n человек. Когда человек заходит в маршрутку, он идет к месту, находящемуся как можно ближе к середине. Если таких мест несколько, человек идет к месту с меньшим номером. Например, если в маршрутке всего 6 мест и места с номерами 3, 4 заняты, то следующий пассажир сядет на место под номером 2.
Когда в маршрутке кончаются места, она уезжает, а на ее место приезжает новая. Василий по очереди записывал, на какие места садятся пассажиры. Что записал Василий?
Формат входных данных
Единственная строка содержит числа n (1 ⩽ n ⩽ 5 ∗ 10^5) и m (1 ⩽ m ⩽ 5 ∗ 10^5) — количество пассажиров, стоящих в очереди, и вместимость маршрутки соответственно.
Формат выходных данных
В i−й строке выведите, на какое место сел i−й пассажир.
Пример
Задача 2
Ограничения
- Время: 1 секунда
- Память: 256 МБ
Условия
Василий — фермер в двумерном государстве под названием Флатландия.
У Василия есть 4n единиц забора, являющихся отрезками длины 1. Единицы забора разрешается располагать только параллельно осям Ox и Oy , при этом крайние точки забора должны оказаться в целочисленных точках.
Василий хочет построить ровно k загонов для овечек, имеющих размеры 1 × 1. Каждый загон обязан иметь форму квадрата.
Какое минимальное количество овечек могут вмещать k загонов, построенных Василием? Какое максимальное количество овечек могут вмещать k загонов, построенных Василием?
Формат входных данных
Первая строка содержит число n — количество четверок однометровых кусочков забора, которыми располагает Василий.
Вторая строка содержит число k — количество загонов, которое хочет построить Василий. Гарантируется, что выполнена цепочка неравенств 1 ⩽ k ⩽ n ⩽ 10^9.
Формат выходных данных
Через пробел выведите минимальную и максимальную суммарные площади загонов, которые могут иметь k загонов, сооруженных Василием.
Пример
Замечания
- В первом примере для минимизации размеров, необходимо сделать размеры загонов 1×1, 2×2 и 2×2, для максимизации — 1×1, 1×1 и 3×3.
- Во втором примере для минимизации размеров, необходимо сделать размеры загонов 3 × 3, 3×3 и 3×3, для максимизации — 1×1, 1×1 и 7×7.
- В третьем примере для минимизации размеров, необходимо сделать размеры загонов 3 × 3, 3×3, 4×4 и 4×4, для максимизации — 1×1, 1×1, 1×1 и 11×11.
Задача 3
Ограничения
- Время: 1 секунда
- Память: 256 МБ
Условия
Изучая древние надписи, историк Василий обнаружил, что все они состоят из n символов, каждый из которых является нулем или единицей.
Как всем известно, чтобы найти количество лет между датами появления двух надписей, необходимо взять все позиции (нумеруются с единицы), в которых строки отличаются, и сложить. Например, строки «011» и «110» отличаются в позициях 1 и 3, поэтому количество лет между их написанием составляет 4 года.
Хоть надписи, над которыми работает Василий, достаточно велики, в них присутствуют достаточно большие блоки из подряд идущих одинаковых символов. Чтобы с ними было удобнее рабо- тать, Василий выписал две последовательности — в одной из них содержатся символы, находящиеся в блоках, а во второй — размеры самих блоков. Например, надпись «111001111» Василий может закодировать как ({1, 0, 1, 1}, {3, 2, 1, 3}) (хотя это далеко не единственный способ кодирования).
Василий очень боится допустить вычислительную ошибку, поэтому обратился за помощью к вам. По последовательностям, кодирующим две надписи, определите, сколько лет прошло между их появлением.
Формат входных данных
Первая строка содержит число n (1 ⩽ n ⩽ 2 ⋅ 10^5) — количество блоков в первой надписи.
Вторая строка содержит символы s1, s2, . . . , sn (si ∈ 0, 1), записанные через пробел, где si — символ, которому равны все символы в i-м блоке первой надписи.
Третья строка содержит числа a1, a2,..., an (1 ⩽ ai ⩽ 10^9), где ai — количество символов в i-м блоке первой надписи.
Следующие три строки содержат информацию о второй надписи в том же формате. Гарантируется, что надписи содержат одинаковое количество символов, не превосходящее 10^9.
Формат выходных данных
Выведите количество лет, прошедшее между появлением этих надписей.
Пример
Замечание
Во втором примере надписи выглядят как «1100011000» и «1111000000». Они отличаются в позициях 3, 4, 6 и 7, поэтому между их появлением прошло 20 лет.
Задача 4
Ограничения
- Время: 1 секунда
- Память: 256 МБ
Условия
Вам необходимо делегировать сотруднику нетривиальную задачу — покрасить забор, состоящий из m досок. Сотрудник не способен ничего делать без мотивации, поэтому вы придумали n мотивирующих цитат, имеющие эффективности p1, p2, ..., pn. В любой момент времени вы можете подойти к сотруднику и произнести любую мотивирующую цитату, не произнесенную ранее.
Посмотрим на произвольный рабочий день. Если за день вы рассказали сотруднику цитаты с номерами i1, i2, ..., ik то он за этот день покраситpi1+ max(0,pi2 − 1) + ...+ max(pik – k + 1) досок. Например, если в первый день рассказать цитаты с эффективностями 9, 4, 7, а во второй день — цитаты с эффективностями 8, 5, 1, то за первый день сотрудник покрасит 9 + 3 + 5 досок, а за второй — 8 + 4 + 0 досок.
Если в какой-то день сотрудника не мотивировать, он ничего не будет делать.
Задача очень важная, необходимо ее выполнить как можно скорее. Определите, сколько дней уйдет на покраску забора при оптимальной своевременной мотивации сотрудника или определите, что мотивирующие цитаты не позволят справиться с данной задачей.
Формат входных данных
Первая строка содержит числа n(3 ⩽ n ⩽ 2⋅10^5) и m( 1 ⩽ m ⩽ 10^9) — количества мотивирующих цитат и досок соответственно.
Вторая строка содержит числа p1, p2, ..., pn( 1 ⩽ pi ⩽ 10^9) — эффективности мотивирующих цитат.
Формат выходных данных
Если справиться с покраской забора невозможно, выведите -1. В противном случае выведите минимальное количество дней, необходимых для выполнения этой задачи.
Примеры
Замечания
- В первом примере можно сказать сотруднику все цитаты в первый же день. После первой цитаты он покрасит 7 досок, после второй — 6, после третьей — 5, таким образом выполнив задачу.
- Во втором примере можно сказать сотруднику две цитаты в первый день и одну цитату во второй. Тогда за первый день он покрасит 7 + 6 = 13 досок, а за второй — 7.
- В третьем примере можно показать, что как бы цитаты не были рассказаны, сотрудник не справится с задачей.
- В четвертом примере в первый можно сказать сотруднику цитаты под номерами 1 и 4, в результате чего он покрасит 9 + 7 = 16 досок, а во второй день — цитаты под номерами 2 и 5, после чего будет покрашено еще 4 + 6 = 10 досок. Нетрудно убедиться, что за один день сотрудник не справится с задачей.
Задача 5
Ограничения
Время: 1 секунда
- Память: 256 МБ
Условия
Внимательно вглядевшись в горы Краснодарского края, вы заметили там необычное тайное государство.
Государство состоит из n небольших поселений, между которыми проведена n − 1 дорога. Гарантируется, что от каждого города возможно добраться до каждого. Иными словами, поселения и дороги между ними образуют структуру дерева.
Поселение под номером 1 является столицей государства и находится на самой большой высоте среди всех поселений. Обозначим высоту, на которой находится поселение v, как v d(v). Поселения расположены таким образом, что для любого пути 1, v1, v2, ...,vt, не содержащего двух одинаковых вершин, выполнено d(vi) = d(1)−i .
Продолжив свои наблюдения, вы обнаружили необычные передвижения между городами, происходящие раз в минуту.
Сначала в какой-то город v вертолет доставляет несколько коробок, а затем их развозят по городам таким образом, что в каждом городе u, находящемся на высоте от d(v)−k до d(v) включительно и достижимом из города v исключительно спуском вниз по горам, оказывается x новых коробок.
Спустя q минут передвижения закончились.
Для каждого города v определите, сколько коробок окажется в городе v после всех передвижений между городами.
Формат входных данных
Первая строка содержит число n — количество поселений в тайном государстве, n ⩽ 200000 .
Каждая из следующих n−1 строк содержит числа u и v(1 ⩽ u, v ⩽ n) — пару поселений, соединенных дорогой. Гарантируется, что поселения и дороги образуют структуру дерева.
Следующая строка содержит число q — количество минут, в течение которых продолжались передвижения между поселениями, q ⩽ 200000.
Каждая из следующих q строк содержит описание очередного передвижения — числа v(1 ⩽ v ⩽ n), k(0 ⩽ k ⩽ 10^9) и x(1 ⩽ x ⩽ 10^9) .
Формат выходных данных
Через пробел выведите числа a1, a2, ...,an, где ai — количество коробок, появившихся в поселении i после всех передвижений.
Пример
Задача 6
Ограничения
Время: 2 секунды
- Память: 512 МБ
Условия
Для того, чтобы пользователи игры «Три в ряд» посещали ее как можно чаще, разработчики ввели ежедневные бонусы за вход в игру.
Если пользователь заходит в игру первый день, он получает за это a1 кристаллов; при входе в игру второй день подряд, игрок получает в этот день a2 кристаллов; ...; при входе в игру n дней подряд, игрок получает за n -й день an кристаллов. Если же игрок ранее заходил хотя бы n дней подряд и продолжает заходить, за каждый новый день он получает an кристаллов. В случае, если пользователь не заходил в игру день, счетчик сбрасывается и при следующем заходе в игру он получит a1 кристаллов.
Василию необходимо ровно x кристаллов для покупки доступа к новым уровням игры. Василий принципиально не хочет, чтобы в какой-либо момент времени у него было больше x кристаллов. Сколько уйдет дней у Василия до того момента, как он получит возможность поиграть на новых уровнях?
Формат входных данных
Первая строка содержит числа n и x — количество различных ежедневных бонусов и количество кристаллов, которое необходимо набрать Василию, соответственно (1 ⩽ n ⩽ 100, 1 ⩽ x ⩽ 10^6).
Вторая строка содержит числа a1, a2, ...,an (1 ⩽ ai ⩽ 1000), где ai — количество кристаллов, которые получит пользователь при входе в игру в очередной день, если до этого заходил в игру ровно i − 1 день подряд.
Формат выходных данных
Выведите минимальное количество дней, необходимое для получения ровно x кристаллов или -1, если получить ровно x кристаллов невозможно.
Пример
Замечания
- В первом примере можно заходить в игру 5 дней подряд — тогда за первый день будет получено 2 кристалла, за второй — 3, за третий — 4, за четвертый — 5, за пятый — 5 (итого 19 кристаллов). Можно показать, что быстрее, чем за 5 дней, 19 кристаллов получить невозможно.
- Во втором примере можно сначала заходить в игру три дня подряд (будет получено 1+2+4 = 7 кристаллов), один день не заходить, а затем снова заходить два дня подряд (будет получено 1 + 2 = 3 кристалла). Можно показать, что быстрее, чем за 6 дней, 10 кристаллов получить невозможно.
Решённые задачи
Я решил задачи 1-3 и частично шестую. Что значит решил - все примеры выполняются кодом. При этом возможно, не учтены краевые случаи. Об этом узнаю после 18 февраля - когда появится обратная связь:
Вообще, экзамены или что-то подобное, давно не делал - когда ставится ограничение на часы, нужно сконцентрироваться и вложить максимум умственных и моральных усилий.
Предыдущий такой опыт был, наверное, на национальном чемпионате профессионального мастерства России WorldSkills Hi-Tech по компетенции "Инженерный дизайн CAD" в 2018 г. в г. Екатеринбург:
Здесь я обращался, скажем так, к техподдержке - экспертам чемпионата. Интересное было время - там, кстати, познакомился с другом, с которым общаемся до сих пор - он как и я имеет техническое инженерное образование, и также изучает IT. Учит только Си-шарп.
Тогда на чемпионате я занял II место среди всех корпораций России. Но готовился в течение года. Посмотрим, к чему приведёт этот экзамен, к которому готовился неделю.
Задача 1
package main
import (
"fmt"
)
func main() {
var (
queue int
capBus int
middleCupBus int
loadCapBus int
)
fmt.Println("Введите через пробел количество человек в очереди и вместимость автобуса:")
fmt.Scan(&queue, &capBus)
if capBus%2 == 0 {
middleCupBus = capBus / 2
} else {
middleCupBus = capBus/2 + 1
}
for queue > 0 {
lastSeat := middleCupBus
for i := 1; loadCapBus < capBus; i++ {
switch {
case capBus%2 == 0:
if i%2 != 0 {
lastSeat = lastSeat - i + 1
} else {
lastSeat = lastSeat + i - 1
}
default:
if i%2 != 0 {
lastSeat = lastSeat + i - 1
} else {
lastSeat = lastSeat - i + 1
}
}
fmt.Println(lastSeat)
loadCapBus++
queue--
if queue == 0 {
break
}
}
loadCapBus = 0
}
}
Примечания
- Чтобы узнать среднее место в автобусе, я использовал условия. Там два варианта событий - если чётное и нечётное число мест в автобусе.
- Далее, в зависимости от того - чётное или нечётное количество человек в очереди, два варианта заполнения автобуса.
- По логике - алгоритм заполнения в автобусе становится проще, если визуализировать заполнение мест. В скобках указан порядок заполнения мест, без скобок - порядковое место в автобусе. Для пятиместного автобуса: 1[4], 2[2], 3[1], 4[3], 5[5]. Для 6-местного автобуса: 1[5], 2[3], 3[1], 4[2], 5[4], 6[6].
Задача 2
package main
import (
"fmt"
)
func main() {
fmt.Println("Введите количество штабелей стен для хлева:")
var n int
fmt.Scan(&n)
fmt.Println("Введите количество отар:")
var k int
fmt.Scan(&k)
totalBars := n * 4
sheepsArrayMin := make([]int, k)
sheepsArrayMax := make([]int, k)
var barsForSheepfold int
count := 1
var squareSheepfold int
Loop:
for {
for i := 0; i < k; i++ {
if sheepsArrayMin[i] != 0 {
barsForSheepfold += -sheepsArrayMin[i] * 4
}
if squareSheepfold != 0 {
squareSheepfold += -sheepsArrayMin[i] * sheepsArrayMin[i]
}
sheepsArrayMin[i] = count
barsForSheepfold += sheepsArrayMin[i] * 4
squareSheepfold += sheepsArrayMin[i] * sheepsArrayMin[i]
if count == 1 {//создаём базовый массив для максимального количества овечек
sheepsArrayMax[i] = sheepsArrayMin[i]
}
if barsForSheepfold == totalBars {
break Loop
}
//fmt.Println(sheepsArrayMin, barsForSheepfold)//визуализация массива
}
count++
}
fmt.Printf("%v ", squareSheepfold)
barsRemains := totalBars - (k-1)*4
sheepsArrayMax[0] = barsRemains / 4
squareSheepfold = 0
for i := 0; i < k; i++ {
squareSheepfold += sheepsArrayMax[i] * sheepsArrayMax[i]
}
fmt.Println(squareSheepfold)
}
Примечание
- В задаче важно понять, что внутренние перегородки в хлеву не возводятся - т.е. учитываются только стены по периметру.
- Ещё из важного - для минимальной площади хлевов я использовал перебор на +1 хлев большего размера, начиная с минимального размера, т.е. хлева размером 1 х 1. Например сперва заполнял массив хлевами с размерами 1 х 1, т.е. для одной овечки. Далее, если оставались бруски, возводил один хлев размером 2 х 2 и проверял, остаются ли доски. Если да - возводил ещё 2 х 2. Когда количество требуемых хлевов закончились, проверял остаются ли доски если простроить один хлев 3 х 3 и т.д.
- Для максимальной площади хлева я брал базовый массив, заполненный хлевами с ячейкой 1 х 1 и заменял один из хлевов размером 1 х 1 хлевом, построенным из максимального количества досок.
Задача 3
package main
import (
"fmt"
)
func main() {
var (
amountBlocksFirst int
amountBlocksSecond int
temp int
)
fmt.Println("Введите количество блоков символов первой надписи:")
fmt.Scan(&amountBlocksFirst)
fmt.Println("Введите символы блоков первой надписи (0/1):")
binaryArrayFirst := make([]int, amountBlocksFirst)
for i := 0; amountBlocksFirst > i; i++ {
fmt.Scan(&temp)
binaryArrayFirst[i] = temp
}
fmt.Println("Введите количество символов блоков первой надписи:")
amountSymInBlocksFirst := make([]int, amountBlocksFirst)
for i := 0; amountBlocksFirst > i; i++ {
fmt.Scan(&temp)
amountSymInBlocksFirst[i] = temp
}
var fullArrayFirst []int
for i := 0; amountBlocksFirst > i; i++ {
for j := 0; amountSymInBlocksFirst[i] > j; j++ {
fullArrayFirst = append(fullArrayFirst, binaryArrayFirst[i])
//fmt.Printf("Ёмкость среза равна %v, его длина равна %v, на итерации: %v.\n", cap(fullArrayFirst), len(fullArrayFirst), j)
}
}
//fmt.Println(fullArrayFirst)
fmt.Println("Введите количество блоков символов второй надписи:")
fmt.Scan(&amountBlocksSecond)
fmt.Println("Введите символы блоков второй надписи (0/1):")
binaryArraySecond := make([]int, amountBlocksSecond)
for i := 0; amountBlocksSecond > i; i++ {
fmt.Scan(&temp)
binaryArraySecond[i] = temp
}
fmt.Println("Введите количество символов блоков второй надписи:")
amountSymInBlocksSecond := make([]int, amountBlocksSecond)
for i := 0; amountBlocksSecond > i; i++ {
fmt.Scan(&temp)
amountSymInBlocksSecond[i] = temp
}
var fullArraySecond []int
for i := 0; amountBlocksSecond > i; i++ {
for j := 0; amountSymInBlocksSecond[i] > j; j++ {
fullArraySecond = append(fullArraySecond, binaryArraySecond[i])
//fmt.Printf("Ёмкость среза равна %v, его длина равна %v, на итерации: %v.\n", cap(fullArraySecond), len(fullArraySecond), j)
}
}
//fmt.Println(fullArraySecond)
//fmt.Println(cap(fullArrayFirst))
//fmt.Println(cap(fullArraySecond))
temp = 0
for i := 0; len(fullArrayFirst) > i; i++ {
if fullArrayFirst[i] != fullArraySecond[i] {
temp += i + 1
}
}
//fmt.Println(fullArrayFirst)
//fmt.Println(fullArraySecond)
fmt.Println(temp)
}
Примечание
- Важно понять, как строятся надписи;
- Задача сводится к объединению двух массивов. Это нужно сделать для каждой строки письменности. А затем сравнить значения массивов;
- Нужно корректно определить формулу сравнения элементов массивов - там не простая закономерность true/false i++;
- Я не разобрался, почему в моих итоговых массивах (срезах) ёмкость на 2 больше длины. Т.о. я сперва использовал функцию cap(array), которая давала не тот результат, который я рассчитывал. Далее использовал функцию len(array) - и результат стал соответствовал тому, что я хотел получить.
Шестая задача
Эту задачу я решил частично - она даёт корректный результат только для первого случая в примерах решения задач.
package main
import (
"fmt"
)
func main() {
fmt.Println("Введите количество ежедневных бонусов и количество кристаллов, необходимых Василию:")
var (
amountDaysBonus int
crystalsDesire int
temp int
сrystalsStock int
daysNeed int
)
fmt.Scan(&amountDaysBonus, &crystalsDesire)
dailyBonusArray := make([]int, amountDaysBonus)
fmt.Println("Введите ежедневные бонусы:")
for i := 0; amountDaysBonus > i; i++ {
fmt.Scan(&temp)
dailyBonusArray[i] = temp
}
//fmt.Println(dailyBonusArray)
daysWithBonus := 1
for i := 0; сrystalsStock < crystalsDesire; i++ {
if i > amountDaysBonus-1 {
i = amountDaysBonus - 1
}
сrystalsStock += dailyBonusArray[i]
//fmt.Println(сrystalsStock, i)
/* if сrystalsStock+dailyBonusArray[i+1] > crystalsDesire {
daysWithBonus = 1
i = 0
continue
}*/
daysWithBonus++
daysNeed++
}
if crystalsDesire == сrystalsStock {
fmt.Println(daysNeed)
} else {
fmt.Println(-1)
}
}
Примечание
В действительности, код для второго случая, будет намного сложнее первого случая. Не уверен, что смог бы решить эту задачу целиком, если бы решал весь экзамен только её.
Общее впечатление и советы
Это был мой первый опыт контеста. Помогла подготовка, о которой я говорил в начале статьи. На что следует обратить внимание при сдаче экзамена:
1. Вывод в терминал
Я привык делать подсказки, которые выводятся в терминал. Например:
fmt.Println("Введите количество ежедневных бонусов и количество кристаллов, необходимых Василию:")// Введите количество ежедневных бонусов и количество кристаллов, необходимых Василию:
Проблема в том, что автотест Академии Тинькофф не примет код с таким выводом. Там не нужно давать подсказок - нужно только то, что сказано в условиях. Т.е. без подсказок сразу вводить информацию. Иначе получите после автотеста Wrong Answer:
Код был рабочий, но в терминал выводил лишнюю информацию. Из-за этого автотест не принимал решения.
2. Длительное авто-тестирование
Автотест Академии Тинькофф длился от нескольких минут до получаса. Имейте это в виду.
3. Насколько умён автотест
В примере ниже - результат автотестирования 6й задачи:
Итак, мы сразу знаем - что в коде для задачи учтены не все случаи. Но автотест принял задачу. Что здесь важно понимать - если задача принята автотестом, потом она пойдёт куратору направления. И он уже будет проверять дальше.
Т.е., вероятно, можно просто подогнать ответы Println'ами (хотя я не проверял эту гипотезу) и обманут автотест, но ответы всё равно отсекутся куратором.
4. Настройте связь с техподдержкой до экзамена
В описании обучения, ещё до старта, есть возможность посмотреть инфо по связи с техподдержкой. Советую найти телеграм-бота техподдержки и задать им тестовый вопрос или просто написать:
Привет, Бро! Я собираюсь сдавать экзамен и проверил, работает ли чат.
Так вы сэкономите пару минут, если потребуется связаться с ними. Вот примерная последовательность:
1. Находите чат в телеграмм
2. Нажимаете /start
3. В меню выбираете "Тинькоф Академия"
4. Выбираете "Вопрос по программированию"
5. Задаёте вопрос
Из дополнительных предложений - позаботьтесь, чтобы Телеграм был установлен на компьютере - для удобства общения с техподдержкой.
5. Общая подготовка к экзамену
Возможно самое главное - помнить, что вы сдаёте экзамен. Здесь стандартная подготовка:
- Не садитесь сдавать экзамен, если плохо себя чувствуете;
- Не садитесь сдавать экзамен голодным, или испытывающим жажду;
- Проверьте, что интернет, окружение на вашем компьютере (программы, которые будете использовать) и "железо" в порядке;
- Обеспечьте информационную тишину - предупредите, что вас три часа не нужно трогать, телефон - в режим полёта и т.д.
- Сделайте гимнастику перед экзаменом. Особенно полезно сделать гимнастику для пальцев, рук и массаж ушей. Да-да, не смейтесь - массаж для ушей улучшает кровообращение и голова начинает думать лучше.
Ещё из рекомендаций - освойте два навыка:
- Скорочтение - даже базовые навыки улучшат ваши результаты.
Кстати, скорочтение работает и в технической литературе, а не только в художественной.
--//--//--
Напоминаю, если захотите купить курс от SkillBox, воспользуйтесь моей реферальной ссылкой. Вы получите огромную скидку на курс и плюс в карму за помощь каналу.
Бро, ты уже здесь? 👉 Подпишись на канал для новичков «Войти в IT» в Telegram, будем изучать IT вместе 👨💻👩💻👨💻