Задача: 663. Equal Tree Partition
Сложность: medium
Дан корень бинарного дерева. Верните true, если можно разделить дерево на два дерева с равными суммами значений после удаления ровно одного ребра из исходного дерева.
Пример:
👨💻 Алгоритм:
1⃣ Вычисление общей суммы:
Напишите функцию для вычисления общей суммы всех узлов дерева.
2⃣ Проверка возможности разделения:
Напишите функцию, чтобы рекурсивно проверить, может ли поддерево быть равно половине общей суммы. Если такое поддерево найдено, значит дерево можно разделить на две части с равными суммами.
3⃣ Валидация и возврат результата:
Проверьте, что общая сумма четная (так как только в этом случае возможно её разделение на две равные части), и используйте функцию проверки поддерева, чтобы определить, можно ли разделить дерево на две части с равными суммами.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан корень бинарного дерева. Верните true, если можно разделить дерево на два дерева с равными суммами значений после удаления ровно одного ребра из исходного дерева.
Пример:
Input: root = [5,10,10,null,null,2,3]
Output: true
Напишите функцию для вычисления общей суммы всех узлов дерева.
Напишите функцию, чтобы рекурсивно проверить, может ли поддерево быть равно половине общей суммы. Если такое поддерево найдено, значит дерево можно разделить на две части с равными суммами.
Проверьте, что общая сумма четная (так как только в этом случае возможно её разделение на две равные части), и используйте функцию проверки поддерева, чтобы определить, можно ли разделить дерево на две части с равными суммами.
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func checkEqualTree(root *TreeNode) bool {
totalSum := sumTree(root)
if totalSum%2 != 0 {
return false
}
target := totalSum / 2
return checkSubtreeSum(root, target, root)
}
func sumTree(node *TreeNode) int {
if node == nil {
return 0
}
return node.Val + sumTree(node.Left) + sumTree(node.Right)
}
func checkSubtreeSum(node *TreeNode, target int, root *TreeNode) bool {
if node == nil {
return false
}
if node != root && sumTree(node) == target {
return true
}
return checkSubtreeSum(node.Left, target, root) || checkSubtreeSum(node.Right, target, root)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 898. Bitwise ORs of Subarrays
Сложность: medium
Если задан целочисленный массив arr, верните количество различных побитовых ИЛИ всех непустых подмассивов arr. Побитовое ИЛИ подмассива - это побитовое ИЛИ каждого целого числа в подмассиве. Побитовым ИЛИ подмассива одного целого числа является это целое число. Подмассив - это непрерывная непустая последовательность элементов в массиве.
Пример:
👨💻 Алгоритм:
1⃣ Создать множество для хранения уникальных результатов побитового ИЛИ.
2⃣ Для каждого элемента массива, вычислить побитовое ИЛИ всех подмассивов, начинающихся с этого элемента.
Добавить результат каждого вычисления в множество.
3⃣ Вернуть размер множества.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Если задан целочисленный массив arr, верните количество различных побитовых ИЛИ всех непустых подмассивов arr. Побитовое ИЛИ подмассива - это побитовое ИЛИ каждого целого числа в подмассиве. Побитовым ИЛИ подмассива одного целого числа является это целое число. Подмассив - это непрерывная непустая последовательность элементов в массиве.
Пример:
Input: arr = [0]
Output: 1
Добавить результат каждого вычисления в множество.
package main
func subarrayBitwiseORs(arr []int) int {
result := make(map[int]struct{})
current := make(map[int]struct{})
for _, num := range arr {
next := make(map[int]struct{})
next[num] = struct{}{}
for x := range current {
next[x|num] = struct{}{}
}
current = next
for x := range current {
result[x] = struct{}{}
}
}
return len(result)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1047. Remove All Adjacent Duplicates In String
Сложность: easy
Вам дана строка s, состоящая из строчных английских букв. Удаление дубликатов заключается в выборе двух соседних и одинаковых букв и их удалении. Мы многократно производим удаление дубликатов в s, пока не перестанем это делать. Верните конечную строку после того, как все такие удаления дубликатов будут произведены. Можно доказать, что ответ уникален.
Пример:
👨💻 Алгоритм:
1⃣ Создай пустой стек для хранения символов строки.
2⃣ Проходи по символам строки, добавляя каждый символ в стек, если он не совпадает с верхним элементом стека, иначе удаляй верхний элемент.
3⃣ После прохождения по строке, собери оставшиеся символы в стеке в результирующую строку и верни ее.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Вам дана строка s, состоящая из строчных английских букв. Удаление дубликатов заключается в выборе двух соседних и одинаковых букв и их удалении. Мы многократно производим удаление дубликатов в s, пока не перестанем это делать. Верните конечную строку после того, как все такие удаления дубликатов будут произведены. Можно доказать, что ответ уникален.
Пример:
Input: stones = [2,7,4,1,8,1]
Output: 1
package main
import (
"fmt"
)
func removeDuplicates(s string) string {
stack := []rune{}
for _, char := range s {
if len(stack) > 0 && stack[len(stack)-1] == char {
stack = stack[:len(stack)-1]
} else {
stack = append(stack, char)
}
}
return string(stack)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
Задача: 1066. Campus Bikes II
Сложность: medium
На кампусе, представленном в виде двумерной сетки, есть n рабочих и m велосипедов, где n <= m. Каждый рабочий и велосипед имеют координаты на этой сетке.
Мы назначаем каждому рабочему уникальный велосипед таким образом, чтобы сумма Манхэттенских расстояний между каждым рабочим и назначенным ему велосипедом была минимальной.
Верните минимально возможную сумму Манхэттенских расстояний между каждым рабочим и назначенным ему велосипедом.
Манхэттенское расстояние между двумя точками p1 и p2 вычисляется как Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|.
Пример:
👨💻 Алгоритм:
1⃣ Для каждого рабочего, начиная с рабочего с индексом 0, пройдите по всем велосипедам и назначьте велосипед рабочему, если он доступен (visited[bikeIndex] = false). После назначения велосипеда отметьте его как недоступный (visited[bikeIndex] = true). Добавьте Манхэттенское расстояние от этого назначения к общей текущей сумме расстояний, представленной currDistanceSum, и выполните рекурсивный вызов для следующего рабочего.
2⃣ Когда рекурсивный вызов завершится, сделайте велосипед снова доступным, установив visited[bikeIndex] в false. Если мы назначили велосипеды всем рабочим, сравните currDistanceSum с smallestDistanceSum и обновите smallestDistanceSum соответственно.
3⃣ Перед назначением любого велосипеда рабочему, проверьте, если currDistanceSum уже больше или равен smallestDistanceSum. Если это так, пропустите остальных рабочих и вернитесь. Это связано с тем, что currDistanceSum может только увеличиваться, и таким образом мы не найдем лучший результат, чем smallestDistanceSum, используя текущую комбинацию рабочих и велосипедов.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
На кампусе, представленном в виде двумерной сетки, есть n рабочих и m велосипедов, где n <= m. Каждый рабочий и велосипед имеют координаты на этой сетке.
Мы назначаем каждому рабочему уникальный велосипед таким образом, чтобы сумма Манхэттенских расстояний между каждым рабочим и назначенным ему велосипедом была минимальной.
Верните минимально возможную сумму Манхэттенских расстояний между каждым рабочим и назначенным ему велосипедом.
Манхэттенское расстояние между двумя точками p1 и p2 вычисляется как Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|.
Пример:
Input: text = "thestoryofleetcodeandme", words = ["story","fleet","leetcode"]
Output: [[3,7],[9,13],[10,17]]
import "math"
type Solution struct {
smallestDistanceSum int
visited []bool
}
func NewSolution() *Solution {
return &Solution{
smallestDistanceSum: math.MaxInt32,
visited: make([]bool, 10),
}
}
func (s *Solution) findDistance(worker, bike []int) int {
return int(math.Abs(float64(worker[0] - bike[0])) + math.Abs(float64(worker[1] - bike[1])))
}
func (s *Solution) minimumDistanceSum(workers [][]int, workerIndex int, bikes [][]int, currDistanceSum int) {
if workerIndex >= len(workers) {
s.smallestDistanceSum = int(math.Min(float64(s.smallestDistanceSum), float64(currDistanceSum)))
return
}
if currDistanceSum >= s.smallestDistanceSum {
return
}
for bikeIndex := 0; bikeIndex < len(bikes); bikeIndex++ {
if !s.visited[bikeIndex] {
s.visited[bikeIndex] = true
s.minimumDistanceSum(workers, workerIndex+1, bikes,
currDistanceSum+s.findDistance(workers[workerIndex], bikes[bikeIndex]))
s.visited[bikeIndex] = false
}
}
}
func (s *Solution) AssignBikes(workers [][]int, bikes [][]int) int {
s.minimumDistanceSum(workers, 0, bikes, 0)
return s.smallestDistanceSum
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1
Задача: 79. Word Search
Сложность: medium
Дана сетка символов размером m на n, называемая board, и строка word. Верните true, если слово word существует в сетке.
Слово можно составить из букв последовательно смежных ячеек, где смежные ячейки находятся рядом по горизонтали или вертикали. Одна и та же ячейка с буквой не может быть использована более одного раза.
Пример:
👨💻 Алгоритм:
1⃣ Общий подход к алгоритмам обратной трассировки: В каждом алгоритме обратной трассировки существует определенный шаблон кода. Например, один из таких шаблонов можно найти в нашем разделе "Рекурсия II". Скелет алгоритма представляет собой цикл, который проходит через каждую ячейку в сетке. Для каждой ячейки вызывается функция обратной трассировки (backtrack()), чтобы проверить, можно ли найти решение, начиная с этой ячейки.
2⃣ Функция обратной трассировки: Эта функция, реализуемая как алгоритм поиска в глубину (DFS), часто представляет собой рекурсивную функцию. Первым делом проверяется, достигнут ли базовый случай рекурсии, когда слово для сопоставления пусто, то есть для каждого префикса слова уже найдено совпадение. Затем проверяется, не является ли текущее состояние недопустимым: либо позиция ячейки выходит за границы доски, либо буква в текущей ячейке не совпадает с первой буквой слова.
3⃣ Исследование и завершение: Если текущий шаг допустим, начинается исследование с использованием стратегии DFS. Сначала текущая ячейка помечается как посещенная, например, любой неалфавитный символ подойдет. Затем осуществляется итерация через четыре возможных направления: вверх, вправо, вниз и влево. Порядок направлений может быть изменен по предпочтениям пользователя. В конце исследования ячейка возвращается к своему исходному состоянию, и возвращается результат исследования.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дана сетка символов размером m на n, называемая board, и строка word. Верните true, если слово word существует в сетке.
Слово можно составить из букв последовательно смежных ячеек, где смежные ячейки находятся рядом по горизонтали или вертикали. Одна и та же ячейка с буквой не может быть использована более одного раза.
Пример:
Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
Output: true
func exist(board [][]byte, word string) bool {
rows := len(board)
if rows == 0 {
return false
}
cols := len(board[0])
var backtrack func(row int, col int, index int) bool
backtrack = func(row int, col int, index int) bool {
if index == len(word) {
return true
}
if row < 0 || row >= rows || col < 0 || col >= cols ||
board[row][col] != word[index] {
return false
}
ret := false
temp := board[row][col]
board[row][col] = '#'
rowOffsets := []int{0, 1, 0, -1}
colOffsets := []int{1, 0, -1, 0}
for d := 0; d < 4; d++ {
ret = backtrack(row+rowOffsets[d], col+colOffsets[d], index+1)
if ret {
break
}
}
board[row][col] = temp
return ret
}
for row := 0; row < rows; row++ {
for col := 0; col < cols; col++ {
if board[row][col] == word[0] && backtrack(row, col, 0) {
return true
}
}
}
return false
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1155. Number of Dice Rolls With Target Sum
Сложность: medium
У вас есть n кубиков, и на каждом кубике k граней, пронумерованных от 1 до k.
Даны три целых числа n, k и target. Необходимо вернуть количество возможных способов (из общего количества kn способов) выбросить кубики так, чтобы сумма выпавших чисел равнялась target. Так как ответ может быть слишком большим, верните его по модулю 10^9 + 7.
Пример:
👨💻 Алгоритм:
1⃣ Начните с:
Индекс кубика diceIndex равен 0; это индекс кубика, который мы рассматриваем в данный момент.
Сумма чисел на предыдущих кубиках currSum равна 0.
Инициализируйте переменную ways значением 0. Итерируйтесь по значениям от 1 до k для каждого значения i. Если текущий кубик может иметь значение i, т.е. currSum после добавления i не превысит значение target, то обновите значение currSum и рекурсивно перейдите к следующему кубику. Добавьте значение, возвращенное этим рекурсивным вызовом, к ways. Иначе, если это значение невозможно, то выйдите из цикла, так как большие значения также не удовлетворят вышеуказанному условию.
2⃣ Базовые случаи:
Если мы перебрали все кубики, т.е. diceIndex = n, то проверьте, равна ли currSum значению target.
3⃣ Верните значение ways и также сохраните его в таблице мемоизации memo, соответствующей текущему состоянию, определяемому diceIndex и currSum.
😎 Решение
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
У вас есть n кубиков, и на каждом кубике k граней, пронумерованных от 1 до k.
Даны три целых числа n, k и target. Необходимо вернуть количество возможных способов (из общего количества kn способов) выбросить кубики так, чтобы сумма выпавших чисел равнялась target. Так как ответ может быть слишком большим, верните его по модулю 10^9 + 7.
Пример:
Input: n = 1, k = 6, target = 3
Output: 1
Explanation: You throw one die with 6 faces.
There is only one way to get a sum of 3.
Индекс кубика diceIndex равен 0; это индекс кубика, который мы рассматриваем в данный момент.
Сумма чисел на предыдущих кубиках currSum равна 0.
Инициализируйте переменную ways значением 0. Итерируйтесь по значениям от 1 до k для каждого значения i. Если текущий кубик может иметь значение i, т.е. currSum после добавления i не превысит значение target, то обновите значение currSum и рекурсивно перейдите к следующему кубику. Добавьте значение, возвращенное этим рекурсивным вызовом, к ways. Иначе, если это значение невозможно, то выйдите из цикла, так как большие значения также не удовлетворят вышеуказанному условию.
Если мы перебрали все кубики, т.е. diceIndex = n, то проверьте, равна ли currSum значению target.
const MOD int = 1e9 + 7
func waysToTarget(memo [][]int, diceIndex, n, currSum, target, k int) int {
if diceIndex == n {
if currSum == target {
return 1
}
return 0
}
if memo[diceIndex][currSum] != -1 {
return memo[diceIndex][currSum]
}
ways := 0
for i := 1; i <= min(k, target-currSum); i++ {
ways = (ways + waysToTarget(memo, diceIndex+1, n, currSum+i, target, k)) % MOD
}
memo[diceIndex][currSum] = ways
return ways
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func numRollsToTarget(n, k, target int) int {
memo := make([][]int, n+1)
for i := range memo {
memo[i] = make([]int, target+1)
for j := range memo[i] {
memo[i][j] = -1
}
}
return waysToTarget(memo, 0, n, 0, target, k)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1012. Numbers With Repeated Digits
Сложность: hard
Задав целое число n, верните количество положительных целых чисел в диапазоне [1, n], у которых хотя бы одна цифра повторяется.
Пример:
👨💻 Алгоритм:
1⃣ Вычисление всех чисел с уникальными цифрами:
Найдите количество чисел в диапазоне [1, n], у которых все цифры уникальны. Для этого используйте перебор всех чисел и проверку уникальности цифр.
2⃣ Вычисление всех чисел в диапазоне [1, n]:
Это значение равно n, поскольку это количество всех чисел от 1 до n включительно.
3⃣ Вычисление результата:
Вычтите количество чисел с уникальными цифрами из общего количества чисел, чтобы получить количество чисел с повторяющимися цифрами.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Задав целое число n, верните количество положительных целых чисел в диапазоне [1, n], у которых хотя бы одна цифра повторяется.
Пример:
Input: n = 20
Output: 1
Найдите количество чисел в диапазоне [1, n], у которых все цифры уникальны. Для этого используйте перебор всех чисел и проверку уникальности цифр.
Это значение равно n, поскольку это количество всех чисел от 1 до n включительно.
Вычтите количество чисел с уникальными цифрами из общего количества чисел, чтобы получить количество чисел с повторяющимися цифрами.
func numDupDigitsAtMostN(n int) int {
return n - countUniqueDigitNumbers(n)
}
func countUniqueDigitNumbers(x int) int {
s := strconv.Itoa(x)
n := len(s)
res := 0
for i := 1; i < n; i++ {
res += 9 * permutation(9, i-1)
}
used := make(map[int]bool)
for i := 0; i < n; i++ {
for j := 0; j < int(s[i]-'0'); j++ {
if i == 0 && j == 0 {
continue
}
if !used[j] {
res += permutation(9-i, n-i-1)
}
}
if used[int(s[i]-'0')] {
break
}
used[int(s[i]-'0')] = true
}
if len(used) == n {
res++
}
return res
}
func permutation(m, n int) int {
if n == 0 {
return 1
}
return permutation(m, n-1) * (m - n + 1)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Задача: 895. Maximum Frequency Stack
Сложность: hard
Разработайте структуру данных, похожую на стек, чтобы заталкивать элементы в стек и вытаскивать из него самый частый элемент. Реализуйте класс FreqStack: FreqStack() строит пустой стек частот. void push(int val) заталкивает целое число val на вершину стека. int pop() удаляет и возвращает самый частый элемент в стеке. Если есть равенство в выборе самого частого элемента, то удаляется и возвращается элемент, который ближе всего к вершине стека.
Пример:
👨💻 Алгоритм:
1⃣ Создать два словаря: freq для хранения частоты каждого элемента и group для хранения стека элементов для каждой частоты.
2⃣ При добавлении элемента увеличивать его частоту в freq и добавлять его в стек соответствующей частоты в group.
3⃣ При извлечении элемента найти максимальную частоту, удалить элемент из стека соответствующей частоты и уменьшить его частоту в freq. Если стек для данной частоты становится пустым, удалить его.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Разработайте структуру данных, похожую на стек, чтобы заталкивать элементы в стек и вытаскивать из него самый частый элемент. Реализуйте класс FreqStack: FreqStack() строит пустой стек частот. void push(int val) заталкивает целое число val на вершину стека. int pop() удаляет и возвращает самый частый элемент в стеке. Если есть равенство в выборе самого частого элемента, то удаляется и возвращается элемент, который ближе всего к вершине стека.
Пример:
Input
["FreqStack", "push", "push", "push", "push", "push", "push", "pop", "pop", "pop", "pop"]
[[], [5], [7], [5], [7], [4], [5], [], [], [], []]
Output
[null, null, null, null, null, null, null, 5, 7, 5, 4]
package main
type FreqStack struct {
freq map[int]int
group map[int][]int
maxfreq int
}
func Constructor() FreqStack {
return FreqStack{
freq: make(map[int]int),
group: make(map[int][]int),
}
}
func (this *FreqStack) Push(val int) {
f := this.freq[val] + 1
this.freq[val] = f
if f > this.maxfreq {
this.maxfreq = f
}
this.group[f] = append(this.group[f], val)
}
func (this *FreqStack) Pop() int {
vals := this.group[this.maxfreq]
val := vals[len(vals)-1]
this.group[this.maxfreq] = vals[:len(vals)-1]
this.freq[val]--
if len(this.group[this.maxfreq]) == 0 {
this.maxfreq--
}
return val
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1129. Shortest Path with Alternating Colors
Сложность: medium
Вам дано целое число n, количество узлов в ориентированном графе, где узлы помечены от 0 до n - 1. Каждое ребро в этом графе может быть красным или синим, и могут быть самопетли и параллельные ребра.
Вам даны два массива redEdges и blueEdges, где:
redEdges[i] = [ai, bi] указывает, что в графе существует направленное красное ребро от узла ai к узлу bi, и
blueEdges[j] = [uj, vj] указывает, что в графе существует направленное синее ребро от узла uj к узлу vj.
Верните массив answer длины n, где каждый answer[x] — это длина кратчайшего пути от узла 0 до узла x, такого что цвета ребер чередуются вдоль пути, или -1, если такого пути не существует.
Пример:
👨💻 Алгоритм:
1⃣ Создание структуры данных и инициализация:
Создайте список смежности adj, который будет содержать пары (сосед, цвет) для каждого узла.
Создайте массив answer длиной n, инициализированный значением -1, чтобы хранить длину кратчайшего пути для каждого узла.
Создайте 2D массив visit для отслеживания, были ли узлы посещены с использованием ребра определённого цвета.
2⃣ Инициализация очереди и начальных условий:
Создайте очередь для хранения трёх значений (узел, количество шагов, цвет предыдущего ребра).
Добавьте в очередь начальный узел (0, 0, -1) и установите visit[0][0] и visit[0][1] в true, так как повторное посещение узла 0 бессмысленно.
3⃣ Обработка очереди и обновление результата:
Пока очередь не пуста, извлекайте элемент из очереди и получайте (узел, количество шагов, цвет предыдущего ребра).
Для каждого соседа, если сосед не был посещён с использованием ребра текущего цвета и текущий цвет не равен предыдущему, обновите массив answer и добавьте соседа в очередь.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вам дано целое число n, количество узлов в ориентированном графе, где узлы помечены от 0 до n - 1. Каждое ребро в этом графе может быть красным или синим, и могут быть самопетли и параллельные ребра.
Вам даны два массива redEdges и blueEdges, где:
redEdges[i] = [ai, bi] указывает, что в графе существует направленное красное ребро от узла ai к узлу bi, и
blueEdges[j] = [uj, vj] указывает, что в графе существует направленное синее ребро от узла uj к узлу vj.
Верните массив answer длины n, где каждый answer[x] — это длина кратчайшего пути от узла 0 до узла x, такого что цвета ребер чередуются вдоль пути, или -1, если такого пути не существует.
Пример:
Input: n = 3, redEdges = [[0,1],[1,2]], blueEdges = []
Output: [0,1,-1]
Создайте список смежности adj, который будет содержать пары (сосед, цвет) для каждого узла.
Создайте массив answer длиной n, инициализированный значением -1, чтобы хранить длину кратчайшего пути для каждого узла.
Создайте 2D массив visit для отслеживания, были ли узлы посещены с использованием ребра определённого цвета.
Создайте очередь для хранения трёх значений (узел, количество шагов, цвет предыдущего ребра).
Добавьте в очередь начальный узел (0, 0, -1) и установите visit[0][0] и visit[0][1] в true, так как повторное посещение узла 0 бессмысленно.
Пока очередь не пуста, извлекайте элемент из очереди и получайте (узел, количество шагов, цвет предыдущего ребра).
Для каждого соседа, если сосед не был посещён с использованием ребра текущего цвета и текущий цвет не равен предыдущему, обновите массив answer и добавьте соседа в очередь.
func shortestAlternatingPaths(n int, redEdges [][]int, blueEdges [][]int) []int {
adj := make(map[int][][]int)
for _, edge := range redEdges {
adj[edge[0]] = append(adj[edge[0]], []int{edge[1], 0})
}
for _, edge := range blueEdges {
adj[edge[0]] = append(adj[edge[0]], []int{edge[1], 1})
}
answer := make([]int, n)
for i := range answer {
answer[i] = -1
}
visit := make([][2]bool, n)
queue := [][3]int{{0, 0, -1}}
answer[0] = 0
visit[0][0] = true
visit[0][1] = true
for len(queue) > 0 {
node, steps, prevColor := queue[0][0], queue[0][1], queue[0][2]
queue = queue[1:]
for _, nei := range adj[node] {
neighbor, color := nei[0], nei[1]
if !visit[neighbor][color] && color != prevColor {
if answer[neighbor] == -1 {
answer[neighbor] = steps + 1
}
visit[neighbor][color] = true
queue = append(queue, [3]int{neighbor, steps + 1, color})
}
}
}
return answer
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 406. Queue Reconstruction by Height
Сложность: medium
Вам дан массив людей, people, которые являются атрибутами некоторых людей в очереди (не обязательно по порядку). Каждый people[i] = [hi, ki] представляет собой человека ростом hi, перед которым стоят ровно ki других людей, чей рост больше или равен hi. Реконструируйте и верните очередь, представленную входным массивом people. Возвращаемая очередь должна быть отформатирована как массив queue, где queue[j] = [hj, kj] - это атрибуты j-го человека в очереди (queue[0] - человек, находящийся в начале очереди).
Пример:
👨💻 Алгоритм:
1⃣ Отсортируйте массив people по убыванию роста hi. Если два человека имеют одинаковый рост, отсортируйте их по возрастанию значения ki.
2⃣ Создайте пустой список для результата. Вставляйте каждого человека из отсортированного массива в список на позицию, соответствующую значению ki.
3⃣ Верните список результата.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вам дан массив людей, people, которые являются атрибутами некоторых людей в очереди (не обязательно по порядку). Каждый people[i] = [hi, ki] представляет собой человека ростом hi, перед которым стоят ровно ki других людей, чей рост больше или равен hi. Реконструируйте и верните очередь, представленную входным массивом people. Возвращаемая очередь должна быть отформатирована как массив queue, где queue[j] = [hj, kj] - это атрибуты j-го человека в очереди (queue[0] - человек, находящийся в начале очереди).
Пример:
Input: people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
Output: [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
package main
import (
"sort"
)
func reconstructQueue(people [][]int) [][]int {
sort.Slice(people, func(i, j int) bool {
if people[i][0] == people[j][0] {
return people[i][1] < people[j][1]
}
return people[i][0] > people[j][0]
})
result := [][]int{}
for _, person := range people {
result = append(result[:person[1]], append([][]int{person}, result[person[1]:]...)...)
}
return result
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 281. Zigzag Iterator
Сложность: medium
Даны два вектора целых чисел v1 и v2, реализуйте итератор, который возвращает их элементы поочередно.
Реализуйте класс ZigzagIterator:
ZigzagIterator(List<int> v1, List<int> v2) инициализирует объект с двумя векторами v1 и v2.
boolean hasNext() возвращает true, если в итераторе еще есть элементы, и false в противном случае.
int next() возвращает текущий элемент итератора и перемещает итератор к следующему элементу.
Пример:
👨💻 Алгоритм:
1⃣ Инициализация объекта:
Создайте класс ZigzagIterator с двумя списками v1 и v2. Сохраните эти списки в структуре vectors.
Инициализируйте очередь queue, содержащую пары индексов: индекс списка и индекс элемента в этом списке, если список не пуст.
2⃣ Метод next:
Удалите первую пару индексов из очереди.
Извлеките элемент из соответствующего списка по указанным индексам.
Если в текущем списке есть еще элементы, добавьте новую пару индексов (тот же список, следующий элемент) в конец очереди.
Верните извлеченный элемент.
3⃣ Метод hasNext:
Проверьте, пуста ли очередь.
Верните true, если в очереди есть элементы, и false в противном случае.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Даны два вектора целых чисел v1 и v2, реализуйте итератор, который возвращает их элементы поочередно.
Реализуйте класс ZigzagIterator:
ZigzagIterator(List<int> v1, List<int> v2) инициализирует объект с двумя векторами v1 и v2.
boolean hasNext() возвращает true, если в итераторе еще есть элементы, и false в противном случае.
int next() возвращает текущий элемент итератора и перемещает итератор к следующему элементу.
Пример:
Input: v1 = [1,2], v2 = [3,4,5,6]
Output: [1,3,2,4,5,6]
Explanation: By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,3,2,4,5,6].
Создайте класс ZigzagIterator с двумя списками v1 и v2. Сохраните эти списки в структуре vectors.
Инициализируйте очередь queue, содержащую пары индексов: индекс списка и индекс элемента в этом списке, если список не пуст.
Удалите первую пару индексов из очереди.
Извлеките элемент из соответствующего списка по указанным индексам.
Если в текущем списке есть еще элементы, добавьте новую пару индексов (тот же список, следующий элемент) в конец очереди.
Верните извлеченный элемент.
Проверьте, пуста ли очередь.
Верните true, если в очереди есть элементы, и false в противном случае.
type ZigzagIterator struct {
vectors [][]int
queue []struct {
vecIndex int
elemIndex int
}
}
func Constructor(v1, v2 []int) ZigzagIterator {
z := ZigzagIterator{
vectors: [][]int{v1, v2},
}
for i, vec := range z.vectors {
if len(vec) > 0 {
z.queue = append(z.queue, struct {
vecIndex int
elemIndex int
}{i, 0})
}
}
return z
}
func (z *ZigzagIterator) next() int {
p := z.queue[0]
z.queue = z.queue[1:]
vecIndex, elemIndex := p.vecIndex, p.elemIndex
nextElemIndex := elemIndex + 1
if nextElemIndex < len(z.vectors[vecIndex]) {
z.queue = append(z.queue, struct {
vecIndex int
elemIndex int
}{vecIndex, nextElemIndex})
}
return z.vectors[vecIndex][elemIndex]
}
func (z *ZigzagIterator) hasNext() bool {
return len(z.queue) > 0
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Задача: 116. Populating Next Right Pointers in Each Node
Сложность: medium
Вам дано идеальное бинарное дерево, где все листья находятся на одном уровне, и у каждого родителя есть два детей. Бинарное дерево имеет следующее определение:
Заполните каждый указатель
Изначально все указатели
Пример:
👨💻 Алгоритм:
1⃣ Инициализируйте очередь Q, которую мы будем использовать во время обхода. Существует несколько способов реализации обхода в ширину, особенно когда речь идет о определении уровня конкретного узла. Можно добавлять в очередь пару (узел, уровень), и каждый раз, когда добавляются дети узла, мы добавляем (node.left, parent_level + 1) и (node.right, parent_level + 1). Этот подход не будет очень эффективен для нашего алгоритма, так как нам нужны все узлы на одном уровне, и для этого потребуется еще одна структура данных.
2⃣ Более эффективный с точки зрения памяти способ разделения узлов одного уровня заключается в использовании некоторой разграничительной метки между уровнями. Обычно мы вставляем в очередь элемент NULL, который отмечает конец предыдущего уровня и начало следующего. Это отличный подход, но он все равно потребует некоторого количества памяти, пропорционально количеству уровней в дереве.
3⃣ Подход, который мы будем использовать здесь, будет иметь структуру вложенных циклов, чтобы обойти необходимость указателя NULL. По сути, на каждом шаге мы записываем размер очереди, который всегда соответствует всем узлам на определенном уровне. Как только мы узнаем этот размер, мы обрабатываем только это количество элементов и не более. К моменту, когда мы закончим обработку заданного количества элементов, в очереди будут все узлы следующего уровня.
Мы начинаем с добавления корня дерева в очередь. Поскольку на уровне 0 есть только один узел, нам не нужно устанавливать какие-либо соединения, и мы можем перейти к циклу while.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вам дано идеальное бинарное дерево, где все листья находятся на одном уровне, и у каждого родителя есть два детей. Бинарное дерево имеет следующее определение:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}Заполните каждый указатель
next так, чтобы он указывал на следующий правый узел. Если следующего правого узла нет, указатель next должен быть установлен в NULL.Изначально все указатели
next установлены в NULL.Пример:
Input: root = [1,2,3,4,5,6,7]
Output: [1,#,2,3,#,4,5,6,7,#]
Explanation: Given the above perfect binary tree (Figure A), your function should populate each next pointer to point to its next right node, just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level.
Мы начинаем с добавления корня дерева в очередь. Поскольку на уровне 0 есть только один узел, нам не нужно устанавливать какие-либо соединения, и мы можем перейти к циклу while.
func connect(root *Node) *Node {
if root == nil {
return root
}
Q := []*Node{root}
for len(Q) > 0 {
size := len(Q)
for i := 0; i < size; i++ {
node := Q[0]
Q = Q[1:]
if i < size-1 {
node.Next = Q[0]
}
if node.Left != nil {
Q = append(Q, node.Left)
}
if node.Right != nil {
Q = append(Q, node.Right)
}
}
}
return root
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 861. Score After Flipping Matrix
Сложность: hard
Дан целочисленный массив nums и целое число k. Верните длину самой короткой непустой подмассива nums, сумма которого составляет как минимум k. Если такого подмассива нет, верните -1.
Подмассив — это непрерывная часть массива.
Пример:
👨💻 Алгоритм:
1⃣ Создайте "моноочередь" индексов P: дек индексов x_0, x_1, ..., так чтобы P[x_0], P[x_1], ... увеличивались.
2⃣ При добавлении нового индекса y, удалите x_i из конца дека, чтобы P[x_0], P[x_1], ..., P[y] увеличивались.
3⃣ Если P[y] >= P[x_0] + K, то (как описано ранее) мы больше не рассматриваем этот x_0 и удаляем его из начала дека.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Дан целочисленный массив nums и целое число k. Верните длину самой короткой непустой подмассива nums, сумма которого составляет как минимум k. Если такого подмассива нет, верните -1.
Подмассив — это непрерывная часть массива.
Пример:
Input: nums = [1], k = 1
Output: 1
func shortestSubarray(A []int, K int) int {
N := len(A)
P := make([]int64, N+1)
for i := 0; i < N; i++ {
P[i+1] = P[i] + int64(A[i])
}
ans := N + 1
monoq := []int{}
for y := 0; y < len(P); y++ {
for len(monoq) > 0 && P[y] <= P[monoq[len(monoq)-1]] {
monoq = monoq[:len(monoq)-1]
}
for len(monoq) > 0 && P[y] >= P[monoq[0]]+int64(K) {
ans = min(ans, y-monoq[0])
monoq = monoq[1:]
}
monoq = append(monoq, y)
}
if ans < N+1 {
return ans
}
return -1
}
func min(a, b int) int {
if a < b {
return a
}
return b
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1662. Check If Two String Arrays are Equivalent
Сложность: easy
Даны два массива строк
Строка представлена массивом, если элементы массива, соединенные в порядке, образуют строку.
Пример:
👨💻 Алгоритм:
1⃣ Построение списка символов для word2:
Создайте список list2 для хранения всех символов из массива строк word2.
2⃣ Итерация по word1 и проверка соответствующих символов:
Итеративно пройдите по строкам в word1 и сравнивайте каждый символ с соответствующим символом из list2.
3⃣ Возврат результата:
Верните true, если все символы совпадают, и false, если найдены несовпадения или длины строк не совпадают.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Даны два массива строк
word1 и word2. Верните true, если два массива представляют одну и ту же строку, и false в противном случае.Строка представлена массивом, если элементы массива, соединенные в порядке, образуют строку.
Пример:
Input: word1 = ["ab", "c"], word2 = ["a", "bc"]
Output: true
Explanation:
word1 represents string "ab" + "c" -> "abc"
word2 represents string "a" + "bc" -> "abc"
The strings are the same, so return true.
Создайте список list2 для хранения всех символов из массива строк word2.
Итеративно пройдите по строкам в word1 и сравнивайте каждый символ с соответствующим символом из list2.
Верните true, если все символы совпадают, и false, если найдены несовпадения или длины строк не совпадают.
func arrayStringsAreEqual(word1 []string, word2 []string) bool {
list2 := strings.Join(word2, "")
index := 0
n := len(list2)
for _, word := range word1 {
for _, char := range word {
if index >= n || byte(char) != list2[index] {
return false
}
index++
}
}
return index == n
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 276. Paint Fence
Сложность: medium
Вы красите забор из n столбцов, используя k различных цветов. Вы должны красить столбы, следуя этим правилам:
Каждый столб должен быть окрашен в один цвет.
Не может быть трех или более подряд идущих столбцов одного цвета.
Учитывая два целых числа n и k, верните количество способов покрасить забор.
Пример:
👨💻 Алгоритм:
1⃣ Инициализация и определение вспомогательной функции:
Определить хеш-таблицу memo, где memo[i] представляет количество способов покрасить i столбцов.
Определить функцию totalWays, которая будет определять количество способов покрасить i столбцов.
2⃣ Реализация базы и проверка кэша:
В функции totalWays проверить базовые случаи: вернуть k, если i == 1, и вернуть k * k, если i == 2.
Проверить, рассчитан ли аргумент i и сохранен ли в memo. Если да, вернуть memo[i].
3⃣ Расчет с использованием рекуррентного соотношения:
В противном случае использовать рекуррентное соотношение для вычисления memo[i], сохранить результат в memo[i] и вернуть его.
Вызвать и вернуть totalWays(n).
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вы красите забор из n столбцов, используя k различных цветов. Вы должны красить столбы, следуя этим правилам:
Каждый столб должен быть окрашен в один цвет.
Не может быть трех или более подряд идущих столбцов одного цвета.
Учитывая два целых числа n и k, верните количество способов покрасить забор.
Пример:
Input: n = 3, k = 2
Output: 6
Explanation: All the possibilities are shown.
Note that painting all the posts red or all the posts green is invalid because there cannot be three posts in a row with the same color.
Определить хеш-таблицу memo, где memo[i] представляет количество способов покрасить i столбцов.
Определить функцию totalWays, которая будет определять количество способов покрасить i столбцов.
В функции totalWays проверить базовые случаи: вернуть k, если i == 1, и вернуть k * k, если i == 2.
Проверить, рассчитан ли аргумент i и сохранен ли в memo. Если да, вернуть memo[i].
В противном случае использовать рекуррентное соотношение для вычисления memo[i], сохранить результат в memo[i] и вернуть его.
Вызвать и вернуть totalWays(n).
func numWays(n int, k int) int {
if n == 1 {
return k
}
twoPostsBack := k
onePostBack := k * k
for i := 3; i <= n; i++ {
curr := (k - 1) * (onePostBack + twoPostsBack)
twoPostsBack = onePostBack
onePostBack = curr
}
return onePostBack
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 409. Longest Palindrome
Сложность: easy
Если задана строка s, состоящая из строчных или прописных букв, верните длину самого длинного палиндрома, который можно построить из этих букв. Буквы чувствительны к регистру, например, "Aa" не считается палиндромом.
Пример:
👨💻 Алгоритм:
1⃣ Создайте словарь для подсчета количества каждого символа в строке.
2⃣ Пройдитесь по словарю и добавьте четное количество каждого символа к длине палиндрома. Если встречается нечетное количество символа, добавьте (count - 1) к длине палиндрома.
3⃣ Если есть хотя бы один символ с нечетным количеством, добавьте 1 к длине палиндрома для центрального символа.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: easy
Если задана строка s, состоящая из строчных или прописных букв, верните длину самого длинного палиндрома, который можно построить из этих букв. Буквы чувствительны к регистру, например, "Aa" не считается палиндромом.
Пример:
Input: s = "abccccdd"
Output: 7
package main
func longestPalindrome(s string) int {
charCount := make(map[rune]int)
for _, char := range s {
charCount[char]++
}
length := 0
oddFound := false
for _, count := range charCount {
if count % 2 == 0 {
length += count
} else {
length += count - 1
oddFound = true
}
}
if oddFound {
return length + 1
}
return length
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 611. Valid Triangle Number
Сложность: medium
Если задан целочисленный массив nums, верните количество выбранных из массива троек, которые могут образовывать треугольники, если принять их за длины сторон треугольника.
Пример:
👨💻 Алгоритм:
1⃣ Отсортируйте массив nums.
2⃣ Используйте три вложенных цикла: для каждого фиксированного третьего элемента, используйте два указателя для поиска подходящих первых двух элементов, которые удовлетворяют неравенству треугольника.
3⃣ Увеличивайте счетчик на количество подходящих пар для каждого третьего элемента.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Если задан целочисленный массив nums, верните количество выбранных из массива троек, которые могут образовывать треугольники, если принять их за длины сторон треугольника.
Пример:
Input: nums = [2,2,3,4]
Output: 3
package main
import (
"sort"
)
func triangleNumber(nums []int) int {
sort.Ints(nums)
n := len(nums)
count := 0
for k := n - 1; k >= 2; k-- {
i := 0
j := k - 1
for i < j {
if nums[i] + nums[j] > nums[k] {
count += j - i
j--
} else {
i++
}
}
}
return count
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 1424. Diagonal Traverse II
Сложность: medium
Дан двумерный целочисленный массив nums, верните все элементы nums в диагональном порядке.
Пример:
👨💻 Алгоритм:
1⃣ Инициализируйте очередь с (0, 0) и список ответов ans.
2⃣ Пока очередь не пуста:
Извлеките (row, col) из очереди.
Добавьте nums[row][col] в ans.
Если col == 0 и row + 1 в пределах массива, добавьте (row + 1, col) в очередь.
Если col + 1 в пределах текущей строки, добавьте (row, col + 1) в очередь.
3⃣ Верните ans.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Дан двумерный целочисленный массив nums, верните все элементы nums в диагональном порядке.
Пример:
Input: nums = [[1,2,3,4,5],[6,7],[8],[9,10,11],[12,13,14,15,16]]
Output: [1,6,2,8,7,3,9,4,12,10,5,13,11,14,15,16]
Извлеките (row, col) из очереди.
Добавьте nums[row][col] в ans.
Если col == 0 и row + 1 в пределах массива, добавьте (row + 1, col) в очередь.
Если col + 1 в пределах текущей строки, добавьте (row, col + 1) в очередь.
func findDiagonalOrder(nums [][]int) []int {
queue := []struct{ row, col int }{{0, 0}}
ans := []int{}
for len(queue) > 0 {
row, col := queue[0].row, queue[0].col
queue = queue[1:]
ans = append(ans, nums[row][col])
if col == 0 && row + 1 < len(nums) {
queue = append(queue, struct{ row, col int }{row + 1, col})
}
if col + 1 < len(nums[row]) {
queue = append(queue, struct{ row, col int }{row, col + 1})
}
}
return ans
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 940. Distinct Subsequences II
Сложность: hard
Поскольку ответ может быть очень большим, верните его по модулю 10^9 + 7. Подпоследовательность строки - это новая строка, которая образуется из исходной строки путем удаления некоторых (можно ни одного) символов без нарушения взаимного расположения оставшихся символов. (Например, "ace" является подпоследовательностью "abcde", а "aec" - нет.
Пример:
👨💻 Алгоритм:
1⃣ Определить матрицу DP, где dp[i][j] будет хранить количество подпоследовательностей строки s с длиной i, оканчивающихся символом j.
2⃣ Инициализировать матрицу DP нулями.
Пройти по каждому символу строки:
Если символ еще не был встречен, все подпоследовательности до текущего символа + текущий символ.
Если символ уже был встречен, учет всех подпоследовательностей, включающих текущий символ, с учетом предыдущих вхождений.
3⃣ Вернуть сумму всех значений в DP по модулю 10^9 + 7.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: hard
Поскольку ответ может быть очень большим, верните его по модулю 10^9 + 7. Подпоследовательность строки - это новая строка, которая образуется из исходной строки путем удаления некоторых (можно ни одного) символов без нарушения взаимного расположения оставшихся символов. (Например, "ace" является подпоследовательностью "abcde", а "aec" - нет.
Пример:
Input: s = "abc"
Output: 7
Пройти по каждому символу строки:
Если символ еще не был встречен, все подпоследовательности до текущего символа + текущий символ.
Если символ уже был встречен, учет всех подпоследовательностей, включающих текущий символ, с учетом предыдущих вхождений.
package main
const MOD = 1000000007
func countSubsequences(s string) int {
dp := make([]int, 26)
for _, c := range s {
index := c - 'a'
sum := 0
for _, val := range dp {
sum = (sum + val) % MOD
}
dp[index] = (sum + 1) % MOD
}
result := 0
for _, val := range dp {
result = (result + val) % MOD
}
return result
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: 983. Minimum Cost For Tickets
Сложность: medium
Вы запланировали несколько поездок на поезде за год вперёд. Дни года, в которые вы будете путешествовать, заданы в виде целочисленного массива days. Каждый день — это целое число от 1 до 365.
Билеты на поезд продаются тремя различными способами:
однодневный билет продаётся за costs[0] долларов,
семидневный билет продаётся за costs[1] долларов, и
тридцатидневный билет продаётся за costs[2] долларов.
Билеты позволяют путешествовать указанное количество дней подряд.
Например, если мы покупаем семидневный билет на 2-й день, то мы можем путешествовать в течение 7 дней: 2, 3, 4, 5, 6, 7 и 8.
Верните минимальное количество долларов, которое вам нужно, чтобы путешествовать каждый день, указанный в списке days.
Пример:
👨💻 Алгоритм:
1⃣ Создайте массив dp размером на один больше последнего дня, в который нужно путешествовать. Инициализируйте все значения -1, что означает, что ответ для этого дня ещё не был вычислен. Также создайте хэш-набор isTravelNeeded из days.
2⃣ Создайте функцию solve, которая принимает аргумент currDay. Если currDay больше последнего дня, когда нужно путешествовать, просто верните 0, так как все дни уже покрыты. Если currDay отсутствует в isTravelNeeded, перейдите к currDay + 1. Если ответ для currDay в массиве dp не равен -1, это означает, что ответ уже был вычислен, поэтому просто верните его.
3⃣ Найдите стоимость трёх билетов, которые можно использовать в этот день, добавьте соответствующую стоимость и обновите dp[currDay] соответственно в рекурсивном вызове. Вызовите solve, передав currDay = 1, и верните ответ.
😎 Решение:
Ставь 👍 и забирай 📚 Базу знаний
Сложность: medium
Вы запланировали несколько поездок на поезде за год вперёд. Дни года, в которые вы будете путешествовать, заданы в виде целочисленного массива days. Каждый день — это целое число от 1 до 365.
Билеты на поезд продаются тремя различными способами:
однодневный билет продаётся за costs[0] долларов,
семидневный билет продаётся за costs[1] долларов, и
тридцатидневный билет продаётся за costs[2] долларов.
Билеты позволяют путешествовать указанное количество дней подряд.
Например, если мы покупаем семидневный билет на 2-й день, то мы можем путешествовать в течение 7 дней: 2, 3, 4, 5, 6, 7 и 8.
Верните минимальное количество долларов, которое вам нужно, чтобы путешествовать каждый день, указанный в списке days.
Пример:
Input: days = [1,4,6,7,8,20], costs = [2,7,15]
Output: 11
Explanation: For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1.
On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9.
On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20.
In total, you spent $11 and covered all the days of your travel.
package main
import "math"
type Solution struct {
isTravelNeeded map[int]bool
}
func (s *Solution) solve(dp []int, days []int, costs []int, currDay int) int {
if currDay > days[len(days)-1] {
return 0
}
if !s.isTravelNeeded[currDay] {
return s.solve(dp, days, costs, currDay+1)
}
if dp[currDay] != -1 {
return dp[currDay]
}
oneDay := costs[0] + s.solve(dp, days, costs, currDay+1)
sevenDay := costs[1] + s.solve(dp, days, costs, currDay+7)
thirtyDay := costs[2] + s.solve(dp, days, costs, currDay+30)
dp[currDay] = int(math.Min(float64(oneDay), math.Min(float64(sevenDay), float64(thirtyDay))))
return dp[currDay]
}
func (s *Solution) MincostTickets(days []int, costs []int) int {
lastDay := days[len(days)-1]
dp := make([]int, lastDay+1)
for i := range dp {
dp[i] = -1
}
s.isTravelNeeded = make(map[int]bool)
for _, day := range days {
s.isTravelNeeded[day] = true
}
return s.solve(dp, days, costs, 1)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1