2020/16
This commit is contained in:
parent
234165c7f3
commit
6443b64932
191
2020/16.cpp
Normal file
191
2020/16.cpp
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <bitset>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <vector>
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
#include <aocpp/Startup.hpp>
|
||||||
|
#include <dlx.hpp>
|
||||||
|
using dlx::Dlx;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ValueType = std::uint64_t;
|
||||||
|
using FieldInfo = std::tuple<std::string, ValueType, ValueType, ValueType, ValueType>;
|
||||||
|
struct Input {
|
||||||
|
std::vector<FieldInfo> ranges;
|
||||||
|
std::vector<ValueType> your_ticket;
|
||||||
|
std::vector<std::vector<ValueType>> nearby_tickets;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ParseTicket(std::string && str) -> std::vector<ValueType>
|
||||||
|
{
|
||||||
|
std::istringstream in{std::move(str)};
|
||||||
|
std::vector<ValueType> result;
|
||||||
|
|
||||||
|
std::string word;
|
||||||
|
while(std::getline(in, word, ',')) {
|
||||||
|
result.push_back(std::stoull(word));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto InRange(
|
||||||
|
FieldInfo const& range,
|
||||||
|
ValueType value
|
||||||
|
) -> bool
|
||||||
|
{
|
||||||
|
auto const& [_,lo1,hi1,lo2,hi2] = range;
|
||||||
|
return lo1 <= value && value <= hi1 || lo2 <= value && value <= hi2;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Parse(std::istream & in) -> Input
|
||||||
|
{
|
||||||
|
Input input;
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
if (line.empty()) break;
|
||||||
|
|
||||||
|
auto start = line.find(':');
|
||||||
|
if (start == std::string::npos || start + 2 > line.size()) {
|
||||||
|
throw std::runtime_error{"bad input"};
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType lo1, hi1, lo2, hi2;
|
||||||
|
auto res = std::sscanf(
|
||||||
|
line.c_str() + start + 2,
|
||||||
|
"%" PRIu64 "-%" PRIu64 " or %" PRIu64 "-%" PRIu64,
|
||||||
|
&lo1, &hi1, &lo2, &hi2);
|
||||||
|
if (res != 4) throw std::runtime_error{"bad input"};
|
||||||
|
|
||||||
|
input.ranges.emplace_back(line.substr(0, start), lo1, hi1, lo2, hi2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::getline(in, line) ||
|
||||||
|
line != "your ticket:" ||
|
||||||
|
!std::getline(in, line)) {
|
||||||
|
throw std::runtime_error{"bad input"};
|
||||||
|
}
|
||||||
|
|
||||||
|
input.your_ticket = ParseTicket(std::move(line));
|
||||||
|
|
||||||
|
if (!std::getline(in, line) ||
|
||||||
|
!line.empty() ||
|
||||||
|
!std::getline(in, line) ||
|
||||||
|
line != "nearby tickets:") {
|
||||||
|
throw std::runtime_error{"bad input"};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
input.nearby_tickets.emplace_back(ParseTicket(std::move(line)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes error rate and removes invalid tickets!
|
||||||
|
auto Part1(Input & input) -> ValueType {
|
||||||
|
ValueType error_rate = 0;
|
||||||
|
|
||||||
|
auto const is_good_value = [&](ValueType value) {
|
||||||
|
return std::any_of(
|
||||||
|
input.ranges.begin(), input.ranges.end(),
|
||||||
|
[value](auto const& x) {
|
||||||
|
return InRange(x, value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = std::remove_if(
|
||||||
|
input.nearby_tickets.begin(), input.nearby_tickets.end(),
|
||||||
|
[&](auto const& ticket) {
|
||||||
|
auto remove_ticket = false;
|
||||||
|
for (auto const value : ticket) {
|
||||||
|
if (!is_good_value(value)) {
|
||||||
|
error_rate += value;
|
||||||
|
remove_ticket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remove_ticket;
|
||||||
|
});
|
||||||
|
input.nearby_tickets.erase(it, input.nearby_tickets.end());
|
||||||
|
return error_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto Part2(Input const& input)
|
||||||
|
{
|
||||||
|
// number of ranges in input
|
||||||
|
auto const n = input.ranges.size();
|
||||||
|
|
||||||
|
// Rows: field_id + n * field_position
|
||||||
|
// Cols: fields then positions
|
||||||
|
Dlx dlx;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < n; i++) {
|
||||||
|
for (std::size_t j = 0; j < n; j++) {
|
||||||
|
auto const possible =
|
||||||
|
std::all_of(
|
||||||
|
input.nearby_tickets.begin(),
|
||||||
|
input.nearby_tickets.end(),
|
||||||
|
[&](auto const& ticket){
|
||||||
|
return InRange(input.ranges[i], ticket[j]);
|
||||||
|
});
|
||||||
|
if (possible) {
|
||||||
|
dlx.Set(i + n*j, i);
|
||||||
|
dlx.Set(i + n*j, n + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType result = 1;
|
||||||
|
dlx::ForallCover(dlx, [&](auto const& sln) {
|
||||||
|
for (auto const x : sln) {
|
||||||
|
auto const field_id = x%n;
|
||||||
|
auto const field_position = x/n;
|
||||||
|
if (std::get<0>(input.ranges[field_id]).starts_with("departure")){
|
||||||
|
result *= input.your_ticket[field_position];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // early exit
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE("documented examples") {
|
||||||
|
TEST_CASE("part 1") {
|
||||||
|
std::istringstream in {
|
||||||
|
"class: 1-3 or 5-7\n"
|
||||||
|
"row: 6-11 or 33-44\n"
|
||||||
|
"seat: 13-40 or 45-50\n"
|
||||||
|
"\n"
|
||||||
|
"your ticket:\n"
|
||||||
|
"7,1,14\n"
|
||||||
|
"\n"
|
||||||
|
"nearby tickets:\n"
|
||||||
|
"7,3,47\n"
|
||||||
|
"40,4,50\n"
|
||||||
|
"55,2,20\n"
|
||||||
|
"38,6,12\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
auto input = Parse(in);
|
||||||
|
REQUIRE(Part1(input) == 71);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto main(int argc, char** argv) -> int {
|
||||||
|
auto input = Parse(aocpp::Startup(argc, argv));
|
||||||
|
std::cout << "Part 1: " << Part1(input) << std::endl;
|
||||||
|
std::cout << "Part 2: " << Part2(input) << std::endl;
|
||||||
|
}
|
42
2020/21.cpp
42
2020/21.cpp
|
@ -14,31 +14,17 @@
|
||||||
#include <doctest.h>
|
#include <doctest.h>
|
||||||
|
|
||||||
#include <aocpp/Startup.hpp>
|
#include <aocpp/Startup.hpp>
|
||||||
|
#include <aocpp/Parsing.hpp>
|
||||||
#include <dlx.hpp>
|
#include <dlx.hpp>
|
||||||
|
|
||||||
using aocpp::Startup;
|
using aocpp::Startup;
|
||||||
|
using aocpp::SplitOn;
|
||||||
using dlx::Dlx;
|
using dlx::Dlx;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Input = std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>>;
|
using Input = std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>>;
|
||||||
|
|
||||||
auto SplitOn(std::string stuff, std::string sep) -> std::vector<std::string> {
|
|
||||||
std::vector<std::string> results;
|
|
||||||
std::size_t cursor = 0;
|
|
||||||
for (;;) {
|
|
||||||
auto i = stuff.find(sep, cursor);
|
|
||||||
if (i != std::string::npos) {
|
|
||||||
results.push_back(stuff.substr(cursor, i-cursor));
|
|
||||||
cursor = i + sep.size();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
results.push_back(stuff.substr(cursor));
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Parse(std::istream & in) -> Input
|
auto Parse(std::istream & in) -> Input
|
||||||
{
|
{
|
||||||
Input results;
|
Input results;
|
||||||
|
@ -123,12 +109,15 @@ auto Part2(Input const& input) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rows: allergen + allergens * ingredient
|
||||||
|
// columns: all allergens, then all ingredients
|
||||||
Dlx dlx;
|
Dlx dlx;
|
||||||
|
|
||||||
for (auto const& [allergen, ingredients] : Arrange(input)) {
|
for (auto const& [allergen, ingredients] : Arrange(input)) {
|
||||||
auto a_id = a_ids[allergen];
|
auto const a_id = a_ids[allergen];
|
||||||
for (auto const& ingredient : ingredients) {
|
for (auto const& ingredient : ingredients) {
|
||||||
auto i_id = i_ids[ingredient];
|
auto const i_id = i_ids[ingredient];
|
||||||
auto row_id = a_id + a_ids.size() * i_id;
|
auto const row_id = a_id + a_ids.size() * i_id;
|
||||||
dlx.Set(row_id, a_id);
|
dlx.Set(row_id, a_id);
|
||||||
dlx.Set(row_id, a_ids.size() + i_id);
|
dlx.Set(row_id, a_ids.size() + i_id);
|
||||||
}
|
}
|
||||||
|
@ -139,22 +128,21 @@ auto Part2(Input const& input) {
|
||||||
dlx.MarkOptional(a_ids.size() + n);
|
dlx.MarkOptional(a_ids.size() + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::size_t> solution;
|
|
||||||
dlx::ForallCover(dlx, [&](auto & sln){ solution = sln; });
|
|
||||||
|
|
||||||
// extract assignment map from dlx row ids
|
// extract assignment map from dlx row ids
|
||||||
std::map<std::string, std::string> entries;
|
std::map<std::string, std::string> entries;
|
||||||
for (auto const row : solution) {
|
dlx::ForallCover(dlx, [&](auto & sln){
|
||||||
entries.emplace(id_to_a[row % a_ids.size()], id_to_i[row / a_ids.size()]);
|
for (auto const row : sln) {
|
||||||
}
|
entries.emplace(id_to_a[row % a_ids.size()], id_to_i[row / a_ids.size()]);
|
||||||
|
}
|
||||||
|
return true; // exit early
|
||||||
|
});
|
||||||
|
|
||||||
// build comma-separated list of ingredients
|
// build comma-separated list of ingredients
|
||||||
std::string result;
|
std::string result;
|
||||||
for (auto const & [_,i] : entries) {
|
for (auto const & [_,i] : entries) {
|
||||||
|
if (!result.empty()) result += ',';
|
||||||
result += i;
|
result += i;
|
||||||
result += ',';
|
|
||||||
}
|
}
|
||||||
result.pop_back();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,8 @@ target_link_libraries(2020_02 aocpp)
|
||||||
add_executable(2020_03 03.cpp)
|
add_executable(2020_03 03.cpp)
|
||||||
target_link_libraries(2020_03 aocpp)
|
target_link_libraries(2020_03 aocpp)
|
||||||
|
|
||||||
|
add_executable(2020_16 16.cpp)
|
||||||
|
target_link_libraries(2020_16 aocpp dlx)
|
||||||
|
|
||||||
add_executable(2020_21 21.cpp)
|
add_executable(2020_21 21.cpp)
|
||||||
target_link_libraries(2020_21 aocpp dlx)
|
target_link_libraries(2020_21 aocpp dlx)
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace dlx {
|
namespace dlx {
|
||||||
|
|
||||||
|
@ -39,8 +40,8 @@ public:
|
||||||
// Increases the number of rows and columns if necessary.
|
// Increases the number of rows and columns if necessary.
|
||||||
auto Set(std::size_t row, std::size_t col) -> void;
|
auto Set(std::size_t row, std::size_t col) -> void;
|
||||||
|
|
||||||
// Marks a column as optional: a solution need not cover the given column,
|
// Marks a column as optional: a solution need not cover the given column,
|
||||||
// but it still must respect the constraints it entails.
|
// but it still must respect the constraints it entails.
|
||||||
auto MarkOptional(std::size_t col) -> void;
|
auto MarkOptional(std::size_t col) -> void;
|
||||||
|
|
||||||
// Removes a row from consideration.
|
// Removes a row from consideration.
|
||||||
|
@ -53,19 +54,19 @@ public:
|
||||||
auto PickRow(std::size_t row) -> void;
|
auto PickRow(std::size_t row) -> void;
|
||||||
|
|
||||||
auto Solve(
|
auto Solve(
|
||||||
std::function<void(std::size_t, std::size_t, std::size_t)> try_cb,
|
std::function<bool(std::size_t, std::size_t, std::size_t)> try_cb,
|
||||||
std::function<void()> undo_cb,
|
std::function<bool()> undo_cb,
|
||||||
std::function<void()> found_cb,
|
std::function<bool()> found_cb,
|
||||||
std::function<void(std::size_t)> stuck_cb) -> void;
|
std::function<bool(std::size_t)> stuck_cb) -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto ForallCover(Dlx& dlx, auto cb) {
|
auto ForallCover(Dlx& dlx, auto cb) {
|
||||||
std::vector<std::size_t> sol;
|
std::vector<std::size_t> sol;
|
||||||
dlx.Solve(
|
dlx.Solve(
|
||||||
[&](std::size_t c, std::size_t s, std::size_t r) { sol.push_back(r); },
|
[&](std::size_t c, std::size_t s, std::size_t r) { sol.push_back(r); return false; },
|
||||||
[&](){ sol.pop_back(); },
|
[&](){ sol.pop_back(); return false; },
|
||||||
[&](){ cb(sol); },
|
[&](){ return cb(std::as_const(sol)); },
|
||||||
[](std::size_t){}
|
[](std::size_t){ return false; }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,43 +228,42 @@ auto Dlx::RemoveRow(std::size_t i) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Dlx::Solve(
|
auto Dlx::Solve(
|
||||||
std::function<void(std::size_t, std::size_t, std::size_t)> try_cb,
|
std::function<bool(std::size_t, std::size_t, std::size_t)> try_cb,
|
||||||
std::function<void()> undo_cb,
|
std::function<bool()> undo_cb,
|
||||||
std::function<void()> found_cb,
|
std::function<bool()> found_cb,
|
||||||
std::function<void(std::size_t)> stuck_cb) -> void
|
std::function<bool(std::size_t)> stuck_cb) -> void
|
||||||
{
|
{
|
||||||
auto const recurse = [&](auto const& self) -> void {
|
auto const recurse = [&](auto const& self) -> bool {
|
||||||
auto c = root_->R;
|
auto c = root_->R;
|
||||||
if (c == root_) {
|
if (c == root_) {
|
||||||
found_cb();
|
return found_cb();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto s = std::numeric_limits<std::size_t>::max();
|
auto s = c->s;
|
||||||
for (auto const i : root_->rightwards()) {
|
for (auto const i : root_->rightwards()) {
|
||||||
if (i->s < s) {
|
if (i->s < s) {
|
||||||
s = (c = i)->s;
|
s = (c = i)->s;
|
||||||
|
|
||||||
if (s == 0) {
|
|
||||||
stuck_cb(c->n);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s == 0) {
|
||||||
|
return stuck_cb(c->n);
|
||||||
|
}
|
||||||
|
|
||||||
c->CoverCol();
|
c->CoverCol();
|
||||||
for (auto const r : c->downwards()) {
|
for (auto const r : c->downwards()) {
|
||||||
try_cb(c->n, s, r->n);
|
if (try_cb(c->n, s, r->n)) [[unlikely]] return true;
|
||||||
for (auto const j : r->rightwards()) {
|
for (auto const j : r->rightwards()) {
|
||||||
j->c->CoverCol();
|
j->c->CoverCol();
|
||||||
}
|
}
|
||||||
self(self);
|
if (self(self)) [[unlikely]] return true;
|
||||||
undo_cb();
|
if (undo_cb()) [[unlikely]] return true;
|
||||||
for (auto const j : r->leftwards()) {
|
for (auto const j : r->leftwards()) {
|
||||||
j->c->UncoverCol();
|
j->c->UncoverCol();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c->UncoverCol();
|
c->UncoverCol();
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
recurse(recurse);
|
recurse(recurse);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
add_library(aocpp src/Startup.cpp src/Coord.cpp)
|
add_library(aocpp src/Startup.cpp src/Coord.cpp src/Parsing.cpp)
|
||||||
target_include_directories(aocpp PUBLIC include)
|
target_include_directories(aocpp PUBLIC include)
|
13
lib/include/aocpp/Parsing.hpp
Normal file
13
lib/include/aocpp/Parsing.hpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef AOCPP_PARSING_HPP_
|
||||||
|
#define AOCPP_PARSING_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace aocpp {
|
||||||
|
|
||||||
|
auto SplitOn(std::string const& stuff, std::string const& sep) -> std::vector<std::string>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
22
lib/src/Parsing.cpp
Normal file
22
lib/src/Parsing.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include <aocpp/Parsing.hpp>
|
||||||
|
|
||||||
|
namespace aocpp {
|
||||||
|
|
||||||
|
auto SplitOn(std::string const& stuff, std::string const& sep) -> std::vector<std::string>
|
||||||
|
{
|
||||||
|
std::vector<std::string> results;
|
||||||
|
std::size_t cursor = 0;
|
||||||
|
for (;;) {
|
||||||
|
auto i = stuff.find(sep, cursor);
|
||||||
|
if (i != std::string::npos) {
|
||||||
|
results.push_back(stuff.substr(cursor, i-cursor));
|
||||||
|
cursor = i + sep.size();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results.push_back(stuff.substr(cursor));
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
Loading…
Reference in New Issue
Block a user