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