Advent of Code 2020 day13
part1
939
7,13,x,x,59,x,31,19
のような入力が与えられる。
1行目はバス乗り場への到着時間、2行目は各バスのID(=運行間隔)を示している。
part1ではx
は無視してよく、それ以外の各IDのバスが0
から開始しそのID時間の間隔で到着する。ID7
のバスは 7, 14, 21, ... , 938, 945, ...
の時間に、ID13
のバスは 13, 26, 39, ..., 936, 949, ...
の時間に来る。
基準の到着時間より後で最も早く到達するバスを求め、そのIDと待ち時間を掛けた値を求めよ、という問題。上の例でいうと 939
以降では ID59
のバスが944
に到着するのが最も早いので (944 - 939) * 59
で 295
が解になる。
問題の通り、すべての有効なIDについて基準のtimestamp以降に現れる時間を算出して最小値を求めれば良い。
基準timestampまでいちいち足していては無駄なので、(timestamp % id != 0
という前提で)(id * (timestamp / id + 1) - timestamp
で各IDの待ち時間を求められる。
struct Solution {
inputs: Vec<String>,
}
impl Solution {
fn new(inputs: Vec<String>) -> Self {
Self { inputs }
}
fn solve_1(&self) -> i32 {
let timestamp = self.inputs[0].parse::<i32>().unwrap();
if let Some((minutes, id)) = self.inputs[1]
.split(',')
.filter_map(|s| s.parse::<i32>().ok())
.map(|id| (id * (timestamp / id + 1) - timestamp, id))
.min_by_key(|&e| e.0)
{
id * minutes
} else {
0
}
}
}
part2
今度は1行目の時間は無視で、2行目のバスIDたちだけを使う。「i
番目のバスがある時刻よりi
経過した時刻に到着する」という条件をすべてのi
が満たす最初の時刻を求めよ、という問題。例の
7,13,x,x,59,x,31,19
で言うと、
-
0
番目の、ID7
のバスが 時刻t + 0
に到着 -
1
番目の、ID13
のバスが 時刻t + 1
に到着 -
2
,3
番目は無視 -
4
番目の、ID59
のバスが 時刻t + 4
に到着 -
5
番目は無視 -
6
番目の、ID31
のバスが 時刻t + 6
に到着 -
7
番目の、ID19
のバスが 時刻t + 7
に到着
をすべて満たす t
を求める必要がある。
これをどうやって求めるか。
まず0
番目を考えると、7
ごとに到着するバスがt
ぴったりになるので 1
番目は 13
ごとに到着するものがt + 1
になるので 4
番目は
簡単に0
番目と1
番目だけを満たすものを考えてみる。0
番目は任意の整数 1
番目に代入して
…と、 0
番目と1
番目を満たす。
そしてその後も等間隔で続くので、
>>> (77 + 0) % 7
0
>>> (77 + 1) % 13
0
続いて 4
番目も満たすものを考えると、
と、 0
, 1
, 4
番目に関する条件を満たすことになる。
>>> (350 + 0) % 7
0
>>> (350 + 1) % 13
0
>>> (350 + 4) % 59
0
同様に求めていくことで 6
番目も満たすものは 7
番目も満たすものは 1068781
が解となる。
>>> (1068781 + 0) % 7
0
>>> (1068781 + 1) % 13
0
>>> (1068781 + 4) % 59
0
>>> (1068781 + 6) % 31
0
>>> (1068781 + 7) % 19
0
Chinese remainder theorem 中国の剰余定理 がまさにこういう考え方、なのかな。
つまり順番に満たすべき
最初は
fn solve_2(&self) -> i64 {
self.inputs[1]
.split(',')
.enumerate()
.filter_map(|(i, s)| {
if let Ok(id) = s.parse::<i64>() {
Some((i as i64, id))
} else {
None
}
})
.fold((1, 0), |(a, b), (i, id)| {
let m = (0..).find(|m| (a * m + b + i) % id == 0).unwrap();
(a * id, a * m + b)
})
.1
}
Discussion