【译】用 Rust 实现 Advent of Code 2020 第9天

108 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情

第 9 天的问题描述有点复杂 —— “啊,也许这就是为什么我通常不做 Advent of Code”的问题所在,但无论如何,还是尝试一下。

我们有像这样的一系列数字:

35
20
15
25
47
40
62
55
65
95
102
117
150
182
127
219
299
277
309
576

前 N 个数字是“元值”,N 后面的数字必须是前面两个数字的和组成。

对于上面的例子,N 是 5。所以,可能有一个聪明和快速的方法来解决这个问题,但我将再次尝试一个简单和正确的解决方案。

我喜欢这个问题的一点是,它可以让我使用一些很酷的方法。

们要遍历 n+1 大小的窗口 —— 所以这里,元素 0..=5 (包括 5) ,然后是 1..=6,然后是 2..=7,以此类推。然后我们会得到元素 0..5,1..6,2..7 等所有可能组合(独家),看看这些组合的总和是否等于我们窗口中的最后一个元素。

use itertools::Itertools;

fn main() {
    let numbers = include_str!("input.txt")
        .lines()
        .map(|x| x.parse::<usize>().unwrap())
        .collect::<Vec<_>>();

    let n = 5;
    let answer = numbers.windows(n + 1).find_map(|s| {
        if (&s[..n])
            .iter()
            .tuple_combinations()
            .any(|(a, b)| a + b == s[n])
        {
            None
        } else {
            Some(s[n])
        }
    });
    println!("answer = {:?}", answer);
}
$ cargo run --quiet
answer = Some(127)

酷熊:酷,这个结果符合预期! 我想我们已经完成了?

让我们试试 n=25 的输入:

$ cargo run --quiet
answer = Some(26134589)

嘿,这是正确的答案!

酷熊:继续!

第二部分

下一部分要求我们在列表中找到“至少两个数字”的连续集合,它们的和与步骤 1 中的无效数字相加”。

这也不是什么难事。我们可以做的一件事是把所有 2 号的窗口加起来,然后把所有 3 号的窗口加起来,以此类推 —— 然后把所有这些窗口中的项加起来。一旦我们找到答案,就可以了!

let answer = answer.unwrap();

let answer2 = (2..numbers.len())
    .into_iter()
    .map(|n| numbers.windows(n).map(|s| s.iter().sum::<usize>()))
    .flatten()
    .find(|&n| n == answer);
println!("answer2 = {:?}", answer2);
$ cargo run --quiet
answer2 = Some(26134589)

我们确实找到了一组连续的数字,它们的和跟我们在第一部分找到的答案是一样的,但我们不知道这组集合在哪里或者有多大。

让我们解决这个问题:

let answer2 = (2..numbers.len())
    .into_iter()
    .map(|n| {
        numbers
            .windows(n)
            .enumerate()
            .map(move |(i, s)| (n, i, s.iter().sum::<usize>()))
    })
    .flatten()
    .find(|&(_, _, sum)| sum == answer);

let (n, i, _) = answer2.unwrap();
let set = &numbers[i..][..n];
println!("sum({:?}) = {}", set, answer);
$ cargo run --quiet
sum([1503494, 978652, 1057251, 1142009, 1239468, 1407633, 1048040, 1484541, 1164289, 1432864, 1792914, 2556472, 2464510, 1750429, 1753116, 1673488, 1685419]) = 26134589

这样好多了。

现在我们需要把这个连续范围内最小和最大的数相加:

let answer3 = set.iter().max().unwrap() + set.iter().min().unwrap();
dbg!(answer3);
$ cargo run --quiet
[src/main.rs:39] answer3 = 3535124

啊,我们完成了! 很容易,看来我的担心是多余的。