Problem:
ダーツ

問題

あなたは以下のルールでダーツゲームをすることになった.

あなたは,矢を的(まと)に向かって 4 本まで投げることができる.必ずしも 4 本全てを投げる必要はなく,1 本も投げなくてもかまわない.的は N 個の部分に区切られていて,各々の部分に点数 P1,..., PN が書か れている.矢が刺さった場所の点数の合計 S があなたの得点の基礎となる.S があらかじめ決められたある点数 M 以下の場合は S がそのままあなたの得点となる.しかし,S が M を超えた場合はあなたの得点は 0 点となる.

的に書かれている点数と M の値が与えられたとき,あなたが得ることのできる点数の最大値を求めるプログラムを作成せよ.

入力

入力は複数のデータセットからなる．各データセットは以下の形式で与えられる．

1 行目には,整数 N (1 ≤ N ≤ 1000) と M (1 ≤ M ≤ 200000000 = 2 × 108)がこの順に空白区切りで書かれている.2 行目以降の第 1 + i 行目 (1 ≤ i ≤ N ) には, Pi (1 ≤ Pi ≤ 100000000 = 108 ) が書かれている.

採点用データのうち, 配点の 20% 分は N ≤ 100 を満たし,配点の 50% 分はN ≤ 300 を満たす.

N, M がともに 0 のとき入力の終了を示す. データセットの数は 10 を超えない．

出力

データセットごとにあなたが得ることのできる点数の最大値を１行に出力する.

入出力例

入力例

4 50
3
14
15
9
3 21
16
11
2
0 0

出力例

48
20

1つ目の例では,15 点の部分に 3 本,3 点の部分に 1 本の矢が刺さった場合にあなたの得点は最大になり,その得点は 48 点である.

2つ目の例では,16 点の場所に 1 本,2 点の場所に 2 本の矢が刺さった場合にあなたの得点は最大になり,その得点は 20 点である.

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in Ruby (judge verdict: Accepted)

# 2分木探索
# m以下の最大の要素を返す
# 組み込みbsearchはfind-minimam
def bsearch(m, arr)
  j = (0...arr.size).bsearch {|i| arr[i] > m}
  if j.nil?
    arr.last
  elsif j > 0
    arr[j - 1]
  else
    false
  end
end

# 4重ループ O(n^4)
def main1(n,m,a)
  max = 0
  0.upto(n) do |i|
    i.upto(n) do |j|
      j.upto(n) do |k|
        k.upto(n) do |l|
          sum = a[i] + a[j] + a[k] + a[l]
          max = sum if sum <= m && sum > max
        end
      end
    end
  end

  max
end

# 3重ループ O(n^3logn)
# a[l] <= m - (a[i] + a[j] + a[k]) を2分木探索
def main2(n,m,a)
  max = 0
  0.upto(n) do |i|
    i.upto(n) do |j|
      j.upto(n) do |k|
        # k.upto(n) do |l|
        buf = m - (a[i] + a[j] + a[k])
        if buf >= 0
          a4 = bsearch(buf, a)
          sum = a[i] + a[j] + a[k] + a4
          max = sum if max < sum
        end
        # end
      end
    end
  end

  max
end

# 2重ループ O(n^2logn)
# 2要素の和をメモ化する
def main3(n,m,a)
  max = 0
  # 2要素の和を格納する配列
  sum_2 = a.dup
  0.upto(n) do |i|
    i.upto(n) do |j|
      a_2 = a[i] + a[j]
      sum_2 << a_2 if a_2 <= m
    end
  end
  sum_2.sort!

  f = sum_2.size - 1
  0.upto(f) do |i|
    another_sum_2 = bsearch(m - sum_2[i], sum_2)
    sum = sum_2[i] + another_sum_2
    max = sum if max < sum
  end

  max
end

loop do
  # 入力
  n,m = gets.split(" ").map(&:to_i)
  break if n == 0
  a = []
  a[0] = 0
  (1..n).each { |i| a[i] = gets.to_i }
  a.sort!

  # main
  puts main3(n,m,a)
end