This commit is contained in:
Eric Mertens 2022-11-20 13:52:18 -08:00
parent 234165c7f3
commit 6443b64932
8 changed files with 271 additions and 54 deletions

191
2020/16.cpp Normal file
View 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;
}

View File

@ -14,31 +14,17 @@
#include <doctest.h>
#include <aocpp/Startup.hpp>
#include <aocpp/Parsing.hpp>
#include <dlx.hpp>
using aocpp::Startup;
using aocpp::SplitOn;
using dlx::Dlx;
namespace {
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
{
Input results;
@ -123,12 +109,15 @@ auto Part2(Input const& input) {
}
}
// rows: allergen + allergens * ingredient
// columns: all allergens, then all ingredients
Dlx dlx;
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) {
auto i_id = i_ids[ingredient];
auto row_id = a_id + a_ids.size() * i_id;
auto const i_id = i_ids[ingredient];
auto const row_id = a_id + a_ids.size() * i_id;
dlx.Set(row_id, a_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);
}
std::vector<std::size_t> solution;
dlx::ForallCover(dlx, [&](auto & sln){ solution = sln; });
// extract assignment map from dlx row ids
std::map<std::string, std::string> entries;
for (auto const row : solution) {
entries.emplace(id_to_a[row % a_ids.size()], id_to_i[row / a_ids.size()]);
}
dlx::ForallCover(dlx, [&](auto & sln){
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
std::string result;
for (auto const & [_,i] : entries) {
if (!result.empty()) result += ',';
result += i;
result += ',';
}
result.pop_back();
return result;
}

View File

@ -4,5 +4,8 @@ target_link_libraries(2020_02 aocpp)
add_executable(2020_03 03.cpp)
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)
target_link_libraries(2020_21 aocpp dlx)

View File

@ -4,6 +4,7 @@
#include <cstddef>
#include <functional>
#include <vector>
#include <utility>
namespace dlx {
@ -39,8 +40,8 @@ public:
// Increases the number of rows and columns if necessary.
auto Set(std::size_t row, std::size_t col) -> void;
// Marks a column as optional: a solution need not cover the given column,
// but it still must respect the constraints it entails.
// Marks a column as optional: a solution need not cover the given column,
// but it still must respect the constraints it entails.
auto MarkOptional(std::size_t col) -> void;
// Removes a row from consideration.
@ -53,19 +54,19 @@ public:
auto PickRow(std::size_t row) -> void;
auto Solve(
std::function<void(std::size_t, std::size_t, std::size_t)> try_cb,
std::function<void()> undo_cb,
std::function<void()> found_cb,
std::function<void(std::size_t)> stuck_cb) -> void;
std::function<bool(std::size_t, std::size_t, std::size_t)> try_cb,
std::function<bool()> undo_cb,
std::function<bool()> found_cb,
std::function<bool(std::size_t)> stuck_cb) -> void;
};
auto ForallCover(Dlx& dlx, auto cb) {
std::vector<std::size_t> sol;
dlx.Solve(
[&](std::size_t c, std::size_t s, std::size_t r) { sol.push_back(r); },
[&](){ sol.pop_back(); },
[&](){ cb(sol); },
[](std::size_t){}
[&](std::size_t c, std::size_t s, std::size_t r) { sol.push_back(r); return false; },
[&](){ sol.pop_back(); return false; },
[&](){ return cb(std::as_const(sol)); },
[](std::size_t){ return false; }
);
}

View File

@ -228,43 +228,42 @@ auto Dlx::RemoveRow(std::size_t i) -> void {
}
auto Dlx::Solve(
std::function<void(std::size_t, std::size_t, std::size_t)> try_cb,
std::function<void()> undo_cb,
std::function<void()> found_cb,
std::function<void(std::size_t)> stuck_cb) -> void
std::function<bool(std::size_t, std::size_t, std::size_t)> try_cb,
std::function<bool()> undo_cb,
std::function<bool()> found_cb,
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;
if (c == root_) {
found_cb();
return;
return found_cb();
}
auto s = std::numeric_limits<std::size_t>::max();
auto s = c->s;
for (auto const i : root_->rightwards()) {
if (i->s < s) {
s = (c = i)->s;
if (s == 0) {
stuck_cb(c->n);
return;
}
}
}
if (s == 0) {
return stuck_cb(c->n);
}
c->CoverCol();
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()) {
j->c->CoverCol();
}
self(self);
undo_cb();
if (self(self)) [[unlikely]] return true;
if (undo_cb()) [[unlikely]] return true;
for (auto const j : r->leftwards()) {
j->c->UncoverCol();
}
}
c->UncoverCol();
return false;
};
recurse(recurse);
}

View File

@ -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)

View 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
View 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