Архив рубрики: Problem solving

Leetcode 1920. Build Array from Permutation

После долгих праздничных выходных — приходится «разогревать» голову задачками с высоким Acceptance. И даже они — не сразу заходят. Со скрипом.

class Solution {
    public static int[] buildArray(int[] nums) {
        int[] ans = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            ans[i] = nums[nums[i]];
        }
        return ans;
    }
}
Given a zero-based permutation nums (0-indexed), build an array ans of the same length where ans[i] = nums[nums[i]] for each 0 <= i < nums.length and return it.

A zero-based permutation nums is an array of distinct integers from 0 to nums.length - 1 (inclusive).

https://leetcode.com/problems/build-array-from-permutation/description/

Leetcode 1360. Number of Days Between Two Dates

Да, с серединки Acceptance начинается интересное уже. Процентов от 65 даже, и в сторону убывания.

Сомневаюсь, конечно, что в секции алгоритмических задач предполагался подобный подход… На это намекает и весьма нескромное время выполнения — 9ms.

Но оно работает. Для начала — уже недурно.

import java.time.Duration;
import java.time.LocalDate;

class Solution {
    public int daysBetweenDates(String date1, String date2) {
        return date1.equals(date2)
        ? 0
        : Math.abs((int) Duration.between(
            LocalDate.parse(date1).atStartOfDay(),
            LocalDate.parse(date2).atStartOfDay()
        ).toDays());
    }
}
Write a program to count the number of days between two dates.

The two dates are given as strings, their format is YYYY-MM-DD as shown in the examples.

https://leetcode.com/problems/number-of-days-between-two-dates/description/

Leetcode 2469. Convert the Temperature

Мда… Надо, похоже, в обратном порядке по Acceptance сортировать. Это слишком уж просто, даже для меня, весьма неискушенного в подобных задачах.

You are given a non-negative floating point number rounded to two decimal places celsius, that denotes the temperature in Celsius.

You should convert Celsius into Kelvin and Fahrenheit and return it as an array ans = [kelvin, fahrenheit].

Return the array ans. Answers within 10^-5 of the actual answer will be accepted.
class Solution {
    public double[] convertTemperature(double celsius) {
        return new double[] {celsius + 273.15d, celsius * 1.80d + 32.00d};
    }
}
https://leetcode.com/problems/convert-the-temperature/description/

Leetcode 1929. Concatenation of Array

В процессе потения над Advent of Code понял, что надо алгосы держать в тонусе — иначе задачи даются тяжко.

Рецепт давно известен — Литкод. Успел там челлендж на первый уровень алгосов пройти и решил, что неплохо бы посохранять это всё где-то. Почему бы не здесь?

Начнём с простого, даже — примитивного. Не особо понял, при чем тут объединение массивов, скорее — генерация какая-то.

class Solution {
    public static int[] getConcatenation(int[] nums) {
        int[] ans = new int[nums.length * 2];
        for (int i = 0; i < nums.length; i++) {
            ans[i] = ans[i + nums.length] = nums[i];
        }
        return ans;
    }
}
https://leetcode.com/problems/concatenation-of-array/description/

Advent of Code 2022: Day 21

Снова мартышки — те, да не те. Эти позадорней, поживей, поактивней 🙂 Но решение чем-то схожее — соседи, операции, кидают банан чиселку, занимаются эквилибристикой. Для этой загадки обвязка получилась потолще, конечно.

Мартышкины ужимки

enum Action {
    ADD('+', Long::sum),
    SUBTRACT('-', (op1, op2) -> op1 - op2),
    DIVIDE('/', (op1, op2) -> op1 / op2),
    MULTIPLY('*', (op1, op2) -> op1 * op2);
    final char code;
    final BiFunction<Long, Long, Long> func;
    static final Map<Character, Action> OPS = Arrays.stream(Action.values())
        .collect(Collectors.toUnmodifiableMap(it -> it.code, it -> it));
    Action(char code, BiFunction<Long, Long, Long> func) {
        this.code = code;
        this.func = func;
    }
    BiFunction<Long, Long, Long> invert(boolean isPrev) {
        switch (this) {
            case ADD: return (op1, op2) -> op2 - op1;
            case MULTIPLY: return (op1, op2) -> op2 / op1;
            case SUBTRACT: return isPrev ? ADD.func : this.func;
            case DIVIDE: return isPrev ? MULTIPLY.func : this.func;
            default: return null;
        }
    }
}
Читать далее Advent of Code 2022: Day 21

Advent of Code 2022: Day 20

Настало Появилось время продолжить AoC за, уже оставшийся в прошлом, 2022 год.

Задачка двадцатого дня была чем-то похожа на обезьяньи игры — потому не вызвала слишком много затруднений.

Упростить решение помогло наличие библиотечного метода Math.floorMod (про который я вычитал в треде на реддите).

Перемешивание координат

static void mix(List<Map.Entry<Integer, Long>> mixed, List<Map.Entry<Integer, Long>> reference) {
    for (var coordinate : reference) {
        int idx = mixed.indexOf(coordinate);
        mixed.remove(idx);
        int newIdx = Math.floorMod(idx + coordinate.getValue(), mixed.size());
        mixed.add(newIdx, coordinate);
    }
}

ЕМНИП, получилось нечто вроде двойного хэширования. Но с ходу приспособить что-то типа LinkedHashMap для хранения координат не удалось, а дольше искать было лень. Так что — остановился на списках.

Вычисление результата

static void day20(String puzzleInputUri) throws IOException, InterruptedException {
    long key = 811589153; // 1 for part 1
    int mixes = 10; // 1 for part 1

    AtomicInteger idx = new AtomicInteger(0);
    var encrypted = client.send(request.uri((URI.create(puzzleInputUri))).build(), BodyHandlers.ofLines())
        .body()
        .map(coord -> Map.entry(idx.getAndIncrement(), Long.parseLong(coord) * key))
        .collect(Collectors.toList());

    var reference = new ArrayList<>(encrypted);
    for (int i = 0; i < mixes; i++) {
        mix(encrypted, reference);
    }

    encrypted.stream()
        .filter(c -> c.getValue() == 0)
        .findAny()
        .map(encrypted::indexOf)
        .map(startIdx -> IntStream.of(startIdx + 1000, startIdx + 2000, startIdx + 3000)
            .map(i -> i % encrypted.size())
            .mapToLong(i -> encrypted.get(i).getValue())
            .sum()
        )
        .ifPresent(System.out::println);
}

Надеюсь, загадки оставшихся дней будут не сильно сложнее 🙂

Исходные данные: https://adventofcode.com/2022/day/20/input

Advent of Code 2022: Day 19

Тяжело дались мне эти игры в Factorio. Хотя, вроде бы, с виду и похожи на слоновьи пляски на вентилях.

В этой задачке тоже пришлось погуглить подсказки, когда нашел варианты отсечек для рекурсии — дело пошло значительно веселее!

Виновница торжества

static int mostGeodes(int ore, int clay, int obsidian, int geode, int oreBot, int clayBot,
                      int obsidianBot, int geodeBot, int time, int maxTime, int[] botsCost) {
    if (time == maxTime) {
        return geode;
    }
    int oreTotal = ore + oreBot;
    int clayTotal = clay + clayBot;
    int obsidianTotal = obsidian + obsidianBot;
    int geodeTotal = geode + geodeBot;

    if (ore >= botsCost[4] && obsidian >= botsCost[5]) {
        return mostGeodes(oreTotal - botsCost[4], clayTotal, obsidianTotal - botsCost[5],
            geodeTotal, oreBot, clayBot, obsidianBot, geodeBot + 1, time + 1, maxTime, botsCost);
    }
    if (clayBot >= botsCost[3] && obsidianBot < botsCost[5] && ore >= botsCost[2] && clay >= botsCost[3]) {
        return mostGeodes(oreTotal - botsCost[2], clayTotal - botsCost[3], obsidianTotal, geodeTotal,
            oreBot, clayBot, obsidianBot + 1, geodeBot, time + 1, maxTime, botsCost);
    }

    int best = 0;
    if (obsidianBot < botsCost[5] && ore >= botsCost[2] && clay >= botsCost[3]) {
        best = Math.max(best, mostGeodes(oreTotal - botsCost[2], clayTotal - botsCost[3], obsidianTotal,
            geodeTotal, oreBot, clayBot, obsidianBot + 1, geodeBot, time + 1, maxTime, botsCost));
    }
    if (clayBot < botsCost[3] && ore >= botsCost[1]) {
        best = Math.max(best, mostGeodes(oreTotal - botsCost[1], clayTotal, obsidianTotal, geodeTotal, oreBot,
            clayBot + 1, obsidianBot, geodeBot, time + 1, maxTime, botsCost));
    }
    if (oreBot < 4 && ore >= botsCost[0]) {
        best = Math.max(best, mostGeodes(oreTotal - botsCost[0], clayTotal, obsidianTotal, geodeTotal,
            oreBot + 1, clayBot, obsidianBot, geodeBot, time + 1, maxTime, botsCost));
    }
    if (ore <= 4) {
        best = Math.max(best, mostGeodes(oreTotal, clayTotal, obsidianTotal, geodeTotal, oreBot, clayBot, obsidianBot,
            geodeBot, time + 1, maxTime, botsCost));
    }
    return best;
}

Сильно сократила решение

static void day19(String puzzleInputUri) throws IOException, InterruptedException {
    var blueprints = client.send(request.uri((URI.create(puzzleInputUri))).build(), BodyHandlers.ofLines())
        .body()
        .map(blueprint -> Arrays.stream(blueprint.split("[^0-9]+")).skip(1).mapToInt(Integer::parseInt).toArray())
        .collect(Collectors.toList());

    int answer1 = 0;
    int answer2 = 1;
    for (int i = 0; i < blueprints.size(); i++) {
        int[] cost = blueprints.get(i);
        answer1 += cost[0] * mostGeodes(0, 0, 0, 0, 1, 0, 0, 0, 0, 24, Arrays.copyOfRange(cost, 1, cost.length));
        if (i < 3) {
            answer2 *= mostGeodes(0, 0, 0, 0, 1, 0, 0, 0, 0, 32, Arrays.copyOfRange(cost, 1, cost.length));
        }
    }
    System.out.printf("Answer 1: %d %nAnswer 2: %d", answer1, answer2);
}
Исходные данные: https://adventofcode.com/2022/day/19/input

Advent of Code 2022: Day 18

Загадка восемнадцатого дня, на мой вкус, могла бы служить образцом для многих предыдущих загадок.

Минимум возни с парсингом ввода и кодированием очередных обходов графа. Максимум размышлений над поисками решения.

В то же время, само решение — не переусложнённое. Похожей по соотношению интерес/сложность для меня была задачка с обработкой сигналов.

Решение тут получилось компактным

static void day18(String puzzleInputUri) throws IOException, InterruptedException {
    List<Point> points = client.send(request.uri((URI.create(puzzleInputUri))).build(), HttpResponse.BodyHandlers.ofLines())
        .body()
        .map(line -> line.split(","))
        .map(xyz -> new Point(Integer.parseInt(xyz[0]), Integer.parseInt(xyz[1]), Integer.parseInt(xyz[2])))
        .collect(Collectors.toUnmodifiableList());
    long area1 = area(points);
    System.out.println("Answer 1: " + area1);
    List<Point> voids = findVoids(new Point(0, 0, 0), makeCube(points));
    System.out.println("Answer 2: " + (area1 - area(voids)));
}
Читать далее Advent of Code 2022: Day 18

Advent of Code 2022: Day 17

Тетрис, больше тетриса! Этот тетрис оказался поголоволомней прошлого. Общая идея была понятна сразу, но «на местности» постоянно вылезали какие-то подводные камешки 🙂

Сначала думал пойти через byte[][], выставляя нужные биты в единицу (на что намекала ширина поля), но закопался в сдвигах (жаль, что ввод не оказался набором сдвиговых операций, или я не разгадал его).

Ну, хотя бы с идеей движения «окном» не промахнулся — во второй части она пригодилась.

Подготовительная работа

enum Move {
    LEFT,
    RIGHT;
    private static final Map<Character, Move> cache = Map.of('<', LEFT, '>', RIGHT);
    static Move of(char code) {
        return cache.get(code);
    }
}

class Triple {
    public int left;
    public int middle;
    public List<Integer> right;
    public Triple(int left, int middle, List<Integer> right) {
        this.left = left;
        this.middle = middle;
        this.right = right;
    }
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Triple)) {
            return false;
        } else {
            Triple other = (Triple) obj;
            return Objects.equals(left, other.left)
                && Objects.equals(middle, other.middle)
                && Objects.equals(right, other.right);
        }
    }
    public int hashCode() {
        return Objects.hashCode(left) ^ Objects.hashCode(middle)
            ^ Objects.hashCode(right);
    }
}

static boolean hasMove(boolean[][] shape, int x, int y, Map<Integer, boolean[]> rows) {
    for (int yIdx = 0; yIdx < shape.length; yIdx++) {
        int currentY = y + (shape.length - yIdx);
        if (rows.containsKey(currentY)) {
            boolean[] shapeRow = shape[yIdx];
            boolean[] fieldRow = rows.get(currentY);
            for (int xIdx = 0; xIdx < shapeRow.length; xIdx++) {
                if (fieldRow[x + xIdx] && shapeRow[xIdx]) return false;
            }
        }
    }
    return true;
}

Triple позаимствовал в Apache Commons, чтобы не изгаляться с импортом в консольный скрипт.

Читать далее Advent of Code 2022: Day 17

Advent of Code 2022: Day 16

Пропустил пятнадцатый день. Не осилил… Пока что! Что ж, настало время дня шестнадцатого.

Правда, здесь тоже не обошлось без «помощи зала» — часть решения пришлось подсмотреть в интернете. Иначе упорно не желала разгадываться вторая половина загадки.

В целом — задачи становятся интересней (и парсинг ввода не причиняет неудобств, как бывало). Но и времени занимают ощутимо больше, чем первая десятка.

Следить за котлом!

record Valve(String name, long flow, List<String> linked) {}
record State(Map<String, Long> openValves, Valve player, Valve elephant, long totalFlow) {}

static List<State> openValve(Valve v1, Valve v2, boolean both, Map<String, Valve> valves, State s, long flow) {
    if(v1.flow() > 0 && !s.openValves().containsKey(v1.name()) && (!both || (v2.flow() > 0 && !s.openValves().containsKey(v2.name())))) {
        Map<String, Long> newOpen = new HashMap<>(s.openValves());
        newOpen.put(v1.name(), v1.flow());
        if (both) {
            newOpen.put(v2.name(), v2.flow());
            return List.of(new State(newOpen, v1, v2, flow));
        }
        return v2.linked().stream().map(name -> new State(newOpen, v1, valves.get(name), flow)).collect(Collectors.toList());
    }
    return List.of();
}
Читать далее Advent of Code 2022: Day 16