87 lines
1.8 KiB
C++
87 lines
1.8 KiB
C++
#include <algorithm>
|
|
#include <bitset>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <iterator>
|
|
#include <vector>
|
|
|
|
#include <doctest.h>
|
|
|
|
#include <aocpp/Startup.hpp>
|
|
|
|
namespace {
|
|
|
|
auto Parse(std::istream & in) -> std::vector<std::int64_t>
|
|
{
|
|
std::vector<int64_t> result;
|
|
std::int64_t x;
|
|
while (in >> x) {
|
|
result.push_back(x);
|
|
}
|
|
std::sort(result.begin(), result.end());
|
|
return result;
|
|
}
|
|
|
|
auto Part1(std::vector<std::int64_t> const& entries) {
|
|
std::bitset<2020> numbers;
|
|
for (auto const i : entries) {
|
|
if (i >= 2020) {
|
|
break;
|
|
}
|
|
numbers.set(i);
|
|
}
|
|
for (auto const i : entries) {
|
|
if (numbers.test(2020 - i)) {
|
|
return (2020 - i) * i;
|
|
}
|
|
}
|
|
throw std::runtime_error{"no part 1 solution"};
|
|
}
|
|
|
|
auto Part2(std::vector<std::int64_t> const& entries) {
|
|
std::bitset<2020> numbers;
|
|
for (auto const i : entries) {
|
|
if (i >= 2020) { break; }
|
|
if (i < 2020) { numbers.set(i); }
|
|
}
|
|
|
|
for (auto it1 = entries.begin(); it1 != entries.end(); it1++) {
|
|
for (auto it2 = std::next(it1); it2 != entries.end(); it2++) {
|
|
auto target = 2020 - (*it1) - (*it2);
|
|
if (target < 0) break;
|
|
if (target < 2020 && numbers.test(target)) { return target * *it1 * *it2;}
|
|
}
|
|
}
|
|
throw std::runtime_error{"no part 2 solution"};
|
|
}
|
|
|
|
}
|
|
|
|
TEST_SUITE("documented examples") {
|
|
std::istringstream in {
|
|
"1721\n"
|
|
"979\n"
|
|
"366\n"
|
|
"299\n"
|
|
"675\n"
|
|
"1456\n"};
|
|
auto entries = Parse(in);
|
|
|
|
TEST_CASE("part 1") {
|
|
REQUIRE(Part1(entries) == 514579);
|
|
}
|
|
|
|
TEST_CASE("part 2") {
|
|
REQUIRE(Part2(entries) == 241861950);
|
|
}
|
|
|
|
}
|
|
|
|
auto Main(std::istream & in) -> void
|
|
{
|
|
auto const entries {Parse(in)};
|
|
std::cout << "Part 1: " << Part1(entries) << std::endl;
|
|
std::cout << "Part 2: " << Part2(entries) << std::endl;
|
|
}
|