問題
単発のvalues で
- 隣り合う 2 つの
日を、 同時に 選ぶことは できません。 1 日 おきでも、 何日か 空けても 構いません - 1 日も
働かなくても 構いません。 valuesが空なら 0 を返します valuesの長さは 0 以上 100 以下、 値は どれも 0 以上 10000 以下の整数です
入力と出力の例
例 1
2 日目の 10 と 5 日目の 9 を
入力
values = [3, 10, 4, 2, 9]出力
19例 2
両端の 5 と 5 を
入力
values = [5, 1, 1, 5]出力
10例 3
予定が 1 日も
入力
values = []出力
0面接での聞かれ方
面接では、
- 「整数の
配列から、 隣り合わないように 選んだ合計の 最大を 求めてください」 - 書き終えた
あとに 「どの 日を 選んだかも返せますか?」と 広げて 聞かれます - 「並びが
輪に なっていて、 最初と 最後も 隣どうしだったら?」と、 決まりを 変えて 聞かれる こともあります
値に
まず 考える手順
段 1
まずは
段 2
前から 1
段 3
表を
ここから解答です。先に自分で書いてから読むと、考え方が残ります。
解答
Python
Python
def no_adjacent_max(values):
before, best = 0, 0
for v in values:
pick = before + v
before = best
best = max(best, pick)
return bestJavaScript
JavaScript
function noAdjacentMax(values) {
let before = 0;
let best = 0;
for (const v of values) {
const pick = before + v;
before = best;
best = Math.max(best, pick);
}
return best;
}best がbefore がpick はbest のままがbest にします。
計算量
- 時間は O(n) です。
並びを 前から 1 回なめるだけです - 使う
記憶は O(1) です。 持ち歩くのは 2 つの 変数だけです - 段 2 の
表を そのまま 持つ 書き方は、 時間は 同じ O(n) で、 記憶は O(n) です。 段 1 の 全部を 試す 書き方は、 時間が O(2ⁿ) です
よく ある間違い
- 1 日
おきの 2 通りだけを比べる。 偶数番目の 合計と 奇数番目の 合計を 比べるだけでは 足りません。 例 2 の [5, 1, 1, 5]では、2 日空けて 両端を 選ぶ 10 が答えです - 大きい
日から 欲張って選ぶ。 一番大きい日を 先に 選ぶと、 その 両隣が 使えなくなります。 両隣の 合計の ほうが 大きいこともあるので、 必ず 2 通りを比べます - 変数を
書き換える 順を 間違える。 beforeを先に 書き換えてしまうと、 今日を 選ぶ 場合の 計算に、 1 日前の 値が 混ざります。 今日を 選ぶ合計を 先に pickに取っておいてから、 2 つを 書き換えます
面接での 話し方の例
書き始める
「選び方を
全部試すと 2 の n 乗通りなので、 前から 1 日ずつ 答えを 決めます。 各日で、 選ばないなら前の 日までの 最大、 選ぶなら 2 日前までの 最大に 今日の 稼ぎを 足した数で、 大きい ほうを 取ります。 使うのは 前の 2 つだけなので、 変数 2 つで O(n) 時間、 O(1) 記憶です。」
書き終えたら、
「例 1 で
たどります。 1 日目は 3、 2 日目は 3 と 10 を 比べて 10、 3 日目は 10 と 3 足す 4 の 7 を 比べて 10 のままです。 4 日目は 10 と 10 足す 2 で 12、 5 日目は 12 と 10 足す 9 の 19 を 比べて 19 です。」
面接の
「表を
埋める 考え方を、 前の 2 つだけを 持つ形まで 縮めました。 輪に なった 場合も、 最初の 日を 除いた 並びと 最後の 日を 除いた 並びで 同じ 関数を 2 回呼べば 解けると 思います。 時間が 残っていれば、 選んだ 日を 返す形にも 直せます。」
ここまで、
