0-⁠1 ナップサックで荷物を同じ重さの 2 組に分ける

荷物の​重さの​並びを、​重さの​合計が​同じ​ 2 組に​分けられるかを​調べます。​「半分の​重さちょうどの​選び方が​あるか」に​言い​換え、​1 次元の​表を​後ろから​埋める​ 0-1 ナップサックの​練習です。

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

問題

引っ越しの荷物を、2 人で同じ重さずつ運びたいと考えています。荷物ごとの重さの並び nums を受け取り、全部の荷物を 2 組に分けて、2 組の重さの合計をちょうど同じにできるなら true、できないなら false を返す関数を書いてください。

  • どの荷物も、どちらか一方の組に必ず入れます。荷物を割ることはできません
  • 片方の組が空になっても構いません (ただし重さは 1 以上なので、空の組と同じ重さにはなりません)
  • nums の長さは 1 以上 200 以下、重さはどれも 1 以上 100 以下の整数です

入力と出力の例

例 1

合計は 12 です。4 と 2 で 6、残りの 3、1、2 でも 6 になります。

入力

nums = [3, 1, 4, 2, 2]

出力

true

例 2

合計は 12 で偶数ですが、どう選んでも 6 ちょうどにはなりません。

入力

nums = [2, 3, 7]

出力

false

例 3

合計の半分は 3 です。1 の荷物を 3 回使えば 3 になりますが、1 つの荷物は 1 回しか使えません。

入力

nums = [1, 5]

出力

false

面接での聞かれ方

面接では、「2 組に分ける」話として出され、ナップサックへの言い換えに気づけるかを見られます。

  • 「正の整数の配列を、和が等しい 2 つの組に分けられるか判定してください」
  • 2 次元の表を書いたあとに、「記憶を 1 次元にできますか?」「そのとき、表をどちらの向きに埋めますか? なぜですか?」と聞かれます。向きの理由を言えるかが、シニアとして見られるところです
  • 「重さの合計がもっと大きかったら?」と、表の大きさの限界を聞かれることもあります

2 組に分けると言われると、組の分け方を全部試したくなります。片方の組が決まればもう片方も決まることに、最初に気づくのが入口です。

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

main.py
def can_split(nums):
    # ここに書きます
    pass


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

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