動的計画法で輪になった並びから隣り合わない合計を最大にする

日ごとの​稼ぎの​予定が​輪のように​繰り返し、​最後の​日と​最初の​日も​隣どうしに​なる​とき、​続けて​ 2 日は​選ばない​決まりで​合計の​最大を​求めます。​輪を​ 2 本の​まっすぐな​並びに​分け、​同じ​表の​埋め方を​ 2 回使う​練習です。

海外で定番の問題リスト 4 本のうち 2 本に入っています (Blind 75、NeetCode 150)

問題

「隣り合わない日を選んで稼ぎを最大にする」問題の続きです。単発の仕事に入れる予定が n 日分あり、日ごとの稼ぎが並び values で決まっています。この予定は、最後の日まで終わるとまた 1 日目から同じ順に繰り返します。そのため、最後の日と次の巡りの 1 日目も、続けて働く 2 日になります。

体を休めるため、続けて 2 日は働けません。ひと巡りの中で働く日をうまく選んだとき、稼ぎの合計の最大を返す関数を書いてください。

  • 隣り合う 2 つの日は、同時に選べません。最後の日と最初の日も隣どうしです
  • 1 日も働かなくても構いません
  • values の長さは 1 以上 100 以下、値はどれも 0 以上 1000 以下の整数です
  • 長さが 1 のときは、その 1 日を選べるものとします。自分自身とは隣どうしと見ません

入力と出力の例

例 1

輪でなければ、1 日目の 4 と 4 日目の 6 で 10 です。ただ、この 2 日は輪の上で隣どうしなので選べません。2 日目の 2 と 4 日目の 6 で 8 が答えです。

入力

values = [4, 2, 3, 6]

出力

8

例 2

6 日でひと巡りなので、1 日目と 4 日目は隣どうしではありません。1 日目の 5 と 4 日目の 6 で 11 です。

入力

values = [5, 1, 2, 6, 2, 3]

出力

11

例 3

予定が 1 日だけなら、その日を選べます。

入力

values = [7]

出力

7

面接での聞かれ方

面接では、まっすぐな並びの版を解いたすぐあとに、決まりを 1 つ変えて出されることが多いです。

  • 「では、並びが輪になっていて、最初と最後も隣どうしだったらどうしますか?」
  • 「最初と最後を両方選んでしまうのを、どう防ぎましたか?」と、場合の分け方を説明させられます
  • 「長さが 1 のときは?」「長さが 2 のときは?」と、端の場合を続けて確かめられます

まっすぐな版の書き方を新しく作り直す必要はありません。前に書いた関数をどう使い回すかを見られています。

アルゴリズムのチートシートの「動的計画法 (DP)」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def circle_max(values):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(circle_max([4, 2, 3, 6]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します