問題
「隣り合わないvalues で
体を
- 隣り合う 2 つの
日は、 同時に 選べません。 最後の 日と 最初の 日も 隣どうしです - 1 日も
働かなくても 構いません valuesの長さは 1 以上 100 以下、 値は どれも 0 以上 1000 以下の整数です - 長さが 1 の
ときは、 その 1 日を 選べる ものとします。 自分 自身とは 隣どうしと見ません
入力と出力の例
例 1
輪でなければ、
入力
values = [4, 2, 3, 6]出力
8例 2
6 日で
入力
values = [5, 1, 2, 6, 2, 3]出力
11例 3
予定が 1 日だけなら、
入力
values = [7]出力
7面接での聞かれ方
面接では、
- 「では、
並びが 輪に なっていて、 最初と 最後も 隣どうしだったら どうしますか?」 - 「最初と
最後を 両方選んでしまうのを、 どう 防ぎましたか?」と、 場合の 分け方を 説明させられます - 「長さが 1 の
ときは?」 「長さが 2 の ときは?」と、 端の 場合を 続けて 確かめられます
まっすぐな
アルゴリズムのチートシートの「動的計画法 (DP)」に、この型の要点を表とコードでまとめています。
まず 考える手順
段 1
まっすぐな
段 2
輪で
段 3
両方の
解答
Python
Python
def line(values, lo, hi):
before, best = 0, 0
for i in range(lo, hi):
v = values[i]
pick = before + v
before = best
best = max(best, pick)
return best
def circle_max(values):
n = len(values)
if n == 1:
return values[0]
return max(
line(values, 0, n - 1),
line(values, 1, n),
)JavaScript
JavaScript
function line(values, lo, hi) {
let before = 0;
let best = 0;
for (; lo < hi; lo++) {
const v = values[lo];
const pick = before + v;
before = best;
best = Math.max(best, pick);
}
return best;
}
function circleMax(values) {
const n = values.length;
if (n === 1) return values[0];
return Math.max(
line(values, 0, n - 1),
line(values, 1, n),
);
}Go
Go
func line(values []int) int {
before, best := 0, 0
for _, v := range values {
pick := before + v
before = best
best = max(best, pick)
}
return best
}
func circleMax(
values []int,
) int {
n := len(values)
if n == 1 {
return values[0]
}
a := line(values[:n-1])
b := line(values[1:])
return max(a, b)
}line は、lo から hi の
計算量
- 時間は O(n) です。
長さ n - 1 の 並びを 2 回なめるだけです - 使う
記憶は O(1) です。 持ち歩くのは 2 つの 変数だけで、 並びの 写しも 作りません - 最初の
日を 選ぶ場合と 選ばない 場合で 表を 2 枚持つ 書き方も できますが、 計算量は 同じで、 書く 量が増えます
よく ある間違い
- まっすぐな
版を そのまま 1 回だけ呼ぶ。 最初と 最後を 両方選んだ 答えが 混ざります。 例 1 の [4, 2, 3, 6]で 10 を返したら、 この 間違いです - 長さ 1 を
分け忘れる。 最初を 除いた 並びも 最後を 除いた 並びも 空に なり、 0 を 返してしまいます。 例 3 の [7]は 7 です - 最初と
最後の 両方を 除いた 並びだけを使う。 最初か 最後の どちらかを 選ぶ 答えを 取り こぼします。 例 2 の 11 は、 最初の 日を 選んでいます - 切り出しで
写しを 作ったのに 記憶を O(1) と言う。 Python の 切り出しや JavaScript の sliceは新しい 並びを 作るので、 記憶は O(n) です。 写しを 作るなら、 計算量も そう言います
解答を動かした版 Python 3.12.14Node.js 22.23.4Go 1.24.6確認日 2026年10月30日
面接での 話し方の例
書き始める
「輪で
困るのは 最初と 最後が 両方選ばれる 場合だけです。 正しい 選び方では、 少なくとも どちらか 一方は 選ばれていないので、 最初を 除いた 並びと 最後を 除いた 並びの 2 本に 分ければ、 どちらも まっすぐな 版で 解けます。 前の 関数を 2 回呼んで 大きい ほうを 取るので、 時間は O(n)、 記憶は O(1) です。 長さ 1 だけは 先に 分けます。」
書き終えたら、
「例 1 の
[4, 2, 3, 6]でたどります。 最後を 除いた [4, 2, 3]では、4 と 3 で 7 です。 最初を 除いた [2, 3, 6]では、2 と 6 で 8 です。 大きい ほうの 8 を 返します。 4 と 6 の 10 は、 輪の 上で 隣どうしなので 出てきません。」
