Advent of Code 2023
Advent of Code 是一个为期 25 天的编程挑战赛,从 12 月 1 日持续到 25 日,每天的东部时间凌晨(北京时间的午后 1 点)发布一道题目。不限制语言,不考究算法,只要能提交正确答案,都可以算作通过并得分。这个活动从 2015 年开始,已经成为了全球程序员的年度盛事之一。
每天的题目会分为两个部分,全部完成可得两颗金星,只完成第一部分则获得一颗银星。解题的时间越早,排名越靠前。第一个解决问题的将获得 100 颗星星,接下来每位递减一颗星星,直到递减到一颗星。也就是说每天最多能获得 200 颗星星。
只有少数大佬是为了冲榜,普通人可以一天一语言完成题目。也可以用奇技淫巧解决问题。可以在 r/adventofcode 上看到各种奇技淫巧。
代码:https://github.com/Selflocking/Code/tree/master/others/AOC2023
Day 1 (C++)
Part 1
给你多行字符串,请找出每行中第一个和最后一个数字,将其组成一个二位数。然后将所有的二位数相加。
例如:
1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet
第一行中的数字是 12,第二行中的数字是 38,第三行中的数字是 15,第四行中的数字是 77。将这些数字相加,得到 142。
第一部分简单,只需要从行首遍历到行未,找到第一个数字和最后一个数字,然后组成二位数相加即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
fstream in;
in.open("input.txt");
if (!in.is_open()) {
cerr << "Failed to open file" << endl;
exit(-1);
}
int ans = 0;
while (!in.eof()) {
string s;
in >> s;
int first = -1;
int last = -1;
for (auto c : s) {
if (c >= '0' && c <= '9') {
if (first == -1) {
first = c - '0';
}
last = c - '0';
}
}
if (first == -1 || last == -1) {
cout << "error";
}
ans += first * 10 + last;
}
cout << ans << endl;
return 0;
}
Part 2
one
, two
, three
, four
, five
, six
, seven
, eight
, 和 nine
这些单词也算数字,例如 two1nine
组成的数字是 29。求所有行组成的数字的和。
这部分我做了很久,我最开始的思路是:对于每一行,先找第一个数字,再找第二个数字。接着找英文单词数字,使用string::find
函数找每个单词的位置,比较哪个才是最小的,哪个是最大的。但是我忽略了每行可能有多个同一单词的情况,例如 one2one
, 正确应该是 11, 我的程序输出的是 12。
看了下 Reddit 上的讨论,有些人是从头遍历字符串,遇到 one
等就将其替换成 1。但是会遇到 twone
这种情况。
最后我的做法是先找到第一个单词,接着将字符串反转,找第一个单词。
#include <algorithm>
#include <bits/stdc++.h>
#include <cctype>
#include <fstream>
#include <iostream>
#include <unordered_map>
using namespace std;
auto NumToString = unordered_map<int, string>{
{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}, {5, "five"},
{6, "six"}, {7, "seven"}, {8, "eight"}, {9, "nine"}};
class Num {
public:
bool used = false;
string::size_type loc = 0;
int value = -1;
bool operator<(const Num &other) const {
return this->loc < other.loc;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
fstream in;
// in.open("example2.txt");
in.open("input.txt");
if (!in.is_open()) {
exit(-1);
}
int ans = 0;
while (!in.eof()) {
string s;
in >> s;
Num first;
Num last;
for (string::size_type i = 0; i < s.size(); ++i) {
auto &c = s[i];
if (c >= '0' && c <= '9') {
Num value{true, i, c - '0'};
if (!first.used) {
first = value;
}
last = value;
}
}
for (auto [n, str] : NumToString) {
auto loc = s.find(str);
if (loc != string::npos) {
Num value{true, loc, n};
if (!first.used) {
first = value;
} else {
first = min(first, value);
}
}
}
reverse(s.begin(), s.end());
for (auto [n, str] : NumToString) {
reverse(str.begin(), str.end());
auto loc = s.find(str);
if (loc != string::npos) {
Num value{true, s.size() - loc - str.size(), n};
if (!last.used) {
last = value;
} else {
last = max(last, value);
}
}
}
if (!first.used || !last.used) {
cerr << "error";
}
auto add = first.value * 10 + last.value;
ans += add;
}
cout << ans << endl;
return 0;
}