Add solution day 5

This commit is contained in:
monoid 2024-12-05 16:33:19 +09:00
parent 49547c9170
commit fa4ca32939
5 changed files with 1690 additions and 0 deletions

28
day_5/example.txt Normal file
View File

@ -0,0 +1,28 @@
47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13
75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47

1378
day_5/input.txt Normal file

File diff suppressed because it is too large Load Diff

90
day_5/solve_1.test.ts Normal file
View File

@ -0,0 +1,90 @@
import { assertArrayIncludes, assertEquals } from "jsr:@std/assert";
import { makeTopologySortOrder, readFormat } from "./solve_1.ts";
Deno.test("readFormat", async () => {
const path = new URL("example.txt", import.meta.url).pathname;
const actual = await readFormat(path);
const expected = {
order: [
{ left: 47, right: 53 },
{ left: 97, right: 13 },
{ left: 97, right: 61 },
{ left: 97, right: 47 },
{ left: 75, right: 29 },
{ left: 61, right: 13 },
{ left: 75, right: 53 },
{ left: 29, right: 13 },
{ left: 97, right: 29 },
{ left: 53, right: 29 },
{ left: 61, right: 53 },
{ left: 97, right: 53 },
{ left: 61, right: 29 },
{ left: 47, right: 13 },
{ left: 75, right: 47 },
{ left: 97, right: 75 },
{ left: 47, right: 61 },
{ left: 75, right: 61 },
{ left: 47, right: 29 },
{ left: 75, right: 13 },
{ left: 53, right: 13 },
],
pageNumbers: [
[75, 47, 61, 53, 29],
[97, 61, 53, 29, 13],
[75, 29, 13],
[75, 97, 47, 61, 53],
[61, 13, 29],
[97, 13, 75, 29, 47],
],
};
assertEquals(actual, expected);
});
Deno.test("makeTopologySortOrder", () => {
const orderMap = new Map<number, Set<number>>([
[1, new Set([2, 3])],
[2, new Set([4])],
[3, new Set([4])],
[4, new Set()],
]);
const actual = makeTopologySortOrder(orderMap);
const expected = [[4, 2, 3, 1], [4, 3, 2, 1]];
// 4 -> 2 -> 3 -> 1
// 4 -> 3 -> 2 -> 1
// multiple valid topological sort order
assertArrayIncludes(expected, [actual]);
});
Deno.test("makeTopologySortOrder with cycle", () => {
const orderMap = new Map<number, Set<number>>([
[1, new Set([2])],
[2, new Set([3])],
[3, new Set([1])],
]);
const actual = makeTopologySortOrder(orderMap);
const expected = [[3, 2, 1], [2, 1, 3], [1, 3, 2]]; // or any valid topological sort order
// 3 -> 1 -> 2 -> 3
// 2 -> 3 -> 1 -> 2
// 1 -> 2 -> 3 -> 1
// multiple valid topological sort order
// The order of the nodes in the cycle is not guaranteed.
assertArrayIncludes(expected, [actual]);
});
Deno.test("makeTopologySortOrder with disconnected graph", () => {
const orderMap = new Map<number, Set<number>>([
[1, new Set([2])],
[3, new Set([4])],
]);
const actual = makeTopologySortOrder(orderMap);
const expected = [
[2, 1, 4, 3],
[2, 4, 1, 3],
[2, 4, 3, 1],
[4, 2, 1, 3],
[4, 2, 3, 1],
[4, 3, 2, 1],
];
assertArrayIncludes(expected, [actual]);
});

108
day_5/solve_1.ts Normal file
View File

@ -0,0 +1,108 @@
export async function readFormat(path: string) {
const data = await Deno.readTextFile(path);
// assume LF line endings.
// TODO: handle CRLF line endings.
const [orderTxt, pageNumbersTxt] = data.split("\n\n");
const order = orderTxt.split("\n").map((line) => {
const [left, right] = line.split("|");
return {
left: parseInt(left.trim()),
right: parseInt(right.trim()),
};
});
const pageNumbers = pageNumbersTxt.split("\n").map((line) => {
const numbers = line.split(",");
return numbers.map((num) => parseInt(num.trim()));
});
return { order, pageNumbers };
}
// topology sort order
export function makeTopologySortOrder(
orderMap: Map<number, Set<number>>,
): number[] {
const visited = new Set<number>();
const sorted = new Array<number>();
for (const key of orderMap.keys()) {
if (!visited.has(key)) {
visit(key);
}
}
function visit(node: number) {
if (visited.has(node)) return;
visited.add(node);
const set = orderMap.get(node);
if (set) {
for (const next of set) {
visit(next);
}
}
sorted.push(node);
}
return sorted;
}
async function main() {
const path = new URL("input.txt", import.meta.url).pathname;
const { order, pageNumbers } = await readFormat(path);
// create order map
const orderMap = new Map<number, Set<number>>();
for (const { left, right } of order) {
const leftSet = orderMap.get(left) ?? new Set<number>();
leftSet.add(right);
orderMap.set(left, leftSet);
}
// it's not transitive closure...
//
// apply transitive closure
// const topologySortOrder = makeTopologySortOrder(orderMap);
// const completedSetMap = new Map<number, Set<number>>();
// for (const num of topologySortOrder) {
// // console.log(num, "\n===");
// const set = orderMap.get(num) ?? new Set<number>();
// const completedSet = completedSetMap.get(num) ?? new Set<number>();
// set.forEach((elem) => completedSet.add(elem));
// for (const next of set) {
// const nextSet = completedSetMap.get(next) ?? new Set<number>();
// nextSet.forEach((elem) => completedSet.add(elem));
// }
// console.log(completedSet);
// // console.log("===");
// completedSetMap.set(num, completedSet);
// }
const completedSetMap = orderMap;
// check order is valid or not by comparing with pageNumbers
function checkPageNumberOrder(pageNumbers: number[]): boolean {
const notAllowed = new Set<number>();
for (let i = pageNumbers.length - 1; i >= 0; i--) {
const current = pageNumbers[i];
if (notAllowed.has(current)) {
return false;
}
notAllowed.add(current);
const biggerThanCurrent = completedSetMap.get(current);
biggerThanCurrent?.forEach((elem) => notAllowed.add(elem));
}
return true;
}
let sum = 0;
for (const pages of pageNumbers) {
if (!checkPageNumberOrder(pages)) {
// console.log("invalid order", pages);
} else {
// get middle number
const middle = pages[pages.length >> 1];
sum += middle;
}
}
console.log(sum);
}
if (import.meta.main) {
main();
}

86
day_5/solve_2.ts Normal file
View File

@ -0,0 +1,86 @@
import { readFormat } from "./solve_1.ts";
async function main() {
const path = new URL("input.txt", import.meta.url).pathname;
const { order, pageNumbers } = await readFormat(path);
// create order map
const orderMap = new Map<number, Set<number>>();
for (const { left, right } of order) {
const leftSet = orderMap.get(left) ?? new Set<number>();
leftSet.add(right);
orderMap.set(left, leftSet);
}
const completedSetMap = orderMap;
// check order is valid or not by comparing with pageNumbers
function checkPageNumberOrder(pageNumbers: number[]): boolean {
const notAllowed = new Set<number>();
for (let i = pageNumbers.length - 1; i >= 0; i--) {
const current = pageNumbers[i];
if (notAllowed.has(current)) {
// console.log("not allowed", current, notAllowed );
return false;
}
notAllowed.add(current);
const biggerThanCurrent = completedSetMap.get(current);
biggerThanCurrent?.forEach((elem) => notAllowed.add(elem));
}
return true;
}
function findErrorIndex(n: number, pages: number[]) {
return pages.findIndex(page => {
const biggerThanCurrent = completedSetMap.get(page);
if (biggerThanCurrent) {
return biggerThanCurrent.has(n);
}
return false;
})
}
function correctPageNumberOrder(pageNumbers: number[]): number[] {
const result = [...pageNumbers];
const notAllowed = new Set<number>();
for (let i = result.length - 1; i >= 0; i--) {
const current = result[i];
if (notAllowed.has(current)) {
const p = result.slice(i + 1);
const errorIndex = findErrorIndex(current, p);
if (errorIndex === undefined) {
// could not happen
throw new Error("cannot correct the order");
}
const ei = errorIndex + i + 1;
result[i] = result[ei];
result[ei] = current;
return correctPageNumberOrder(result);
}
notAllowed.add(current);
const biggerThanCurrent = completedSetMap.get(current);
biggerThanCurrent?.forEach((elem) => notAllowed.add(elem));
}
return result;
}
let sum = 0;
for (const pages of pageNumbers) {
if (!checkPageNumberOrder(pages)) {
// correct the order
const corrected = correctPageNumberOrder(pages);
if (!checkPageNumberOrder(corrected)) {
console.log("cannot correct the order", pages);
console.log("corrected", corrected);
throw new Error("cannot correct the order");
}
const middle = corrected[corrected.length >> 1];
sum += middle;
}
}
console.log(sum);
}
if (import.meta.main) {
main();
}