-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathday_11b.cpp
151 lines (140 loc) · 6.06 KB
/
day_11b.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <algorithm>
#include <cmath>
#include <fstream>
#include <iostream>
#include <memory>
#include <queue>
#include <ranges>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// Create a tree like structure
// Expand via BFS not DFS (this is important for memonization)
// Each blink can create a maximum of 2 nodes for each node that exists
// Maintain a list of engravings seen so far
// Each time a blink creates a node with an engraving that has already been seen, do not create a new node for this engraving and let the parent node point to the previously created node with the engraving
// This removes the need to futher expand the new node as the previously created node is guaranteed to be expanded more times than the node that would have been created
// using BFS instead of DFS ensures that every engraving number is seen at the lowest possible blink ensuring that it will undergo more blink operations than any other node with the same engraving that is encountered later
// Count by maintaining a value for the depth in the tree so that when a newer node loops back to a higher/older node, the depth is not lost
// Use memoization by storing a map of engraving -> (level, count_below)
constexpr int n_level = 75;
struct Node {
long long engraving;
Node* left;
Node* right;
};
std::queue<std::pair<Node*, int>> q;
void blink_n(std::vector<std::unique_ptr<Node>>& nodes, std::unordered_map<long long, Node*>& seen) {
while(!q.empty()) {
auto [node, level] = q.front();
q.pop();
if (level >= 75) continue;
int n_digits = 0;
{
auto engraving = node->engraving;
while(engraving > 0) {
n_digits++;
engraving /= 10;
}
}
if (node->engraving == 0) {
auto new_node = std::make_unique<Node>();
new_node->engraving = 1;
if (!seen.contains(new_node->engraving)) {
nodes.push_back(std::move(new_node));
node->left = nodes.back().get();
seen[nodes.back().get()->engraving] = nodes.back().get();
q.push({nodes.back().get(), level+1});
} else {
node->left = seen[new_node->engraving];
}
} else if (n_digits % 2 == 0) {
{
auto new_node = std::make_unique<Node>();
new_node->engraving = node->engraving / static_cast<long long>(std::pow(10, n_digits / 2));
if (!seen.contains(new_node->engraving)) {
nodes.push_back(std::move(new_node));
node->left = nodes.back().get();
seen[nodes.back().get()->engraving] = nodes.back().get();
q.push({nodes.back().get(), level+1});
} else {
node->left = seen[new_node->engraving];
}
}
{
auto new_node = std::make_unique<Node>();
new_node->engraving = node->engraving % static_cast<long long>(std::pow(10, n_digits / 2));
if (!seen.contains(new_node->engraving)) {
nodes.push_back(std::move(new_node));
node->right = nodes.back().get();
seen[nodes.back().get()->engraving] = nodes.back().get();
q.push({nodes.back().get(), level+1});
} else {
node->right = seen[new_node->engraving];
}
}
} else {
auto new_node = std::make_unique<Node>();
new_node->engraving = node->engraving * 2024;
if (!seen.contains(new_node->engraving)) {
nodes.push_back(std::move(new_node));
node->left = nodes.back().get();
seen[nodes.back().get()->engraving] = nodes.back().get();
q.push({nodes.back().get(), level+1});
} else {
node->left = seen[new_node->engraving];
}
}
}
}
std::unordered_map<long long, std::unordered_map<long long, long long>> memory;
long long count (Node * node, const int level){
// if (level == n_level ) std::cout << node->engraving << ' ';
if (level == n_level) return 1;
if (memory.contains(node->engraving) && memory[node->engraving].contains(level)) return memory[node->engraving][level];
if (node->left == nullptr && node->right == nullptr) return 1;
long long n = 0;
if (node->left != nullptr) n += count(node->left, level+1);
if (node->right != nullptr) n += count(node->right, level+1);
memory[node->engraving][level] = n;
return n;
}
int main(int argc, char* argv[]) {
std::string input = "../input/day_11_input";
if (argc > 1) {
input = argv[1];
}
std::ifstream file(input);
std::string line;
while(std::getline(file, line)){
std::vector<std::unique_ptr<Node>> nodes;
long long current = 0;
long long idx = line.find(' ');
while(idx != std::string::npos) {
nodes.emplace_back();
nodes.back() = std::make_unique<Node>();
nodes.back()->engraving = std::stoi(line.substr(current, idx - current));
current = idx + 1;
idx = line.find(' ', current);
}
nodes.emplace_back();
nodes.back() = std::make_unique<Node>();
nodes.back()->engraving = std::stoi(line.substr(current, line.size() - current));
const int n = nodes.size();
long long answer = 0;
for (int i = 0; i < n; i++){
std::vector<std::unique_ptr<Node>> nodes_temp;
nodes_temp.push_back(std::move(nodes[i]));
std::unordered_map<long long, Node*> seen;
q = {};
q.push(std::make_pair<Node*, int>(nodes_temp.back().get(), 0));
seen[nodes_temp.back().get()->engraving] = nodes_temp.back().get();
blink_n(nodes_temp, seen);
answer += count(nodes_temp[0].get(), 0);
}
// std::cout << '\n';
std::cout << std::fixed << answer << '\n';
}
return 0;
}