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 C (judge verdict: Accepted)

#include <stdio.h>
#include <stdlib.h>

#define trade(a,b) tmp=(a);(a)=(b);(b)=tmp;

long M;

int sort(long l[],int N){
        int i=1,j,k;
        long tmp;

        for(i=0;i<N;i++){
                for(j=i;j>0;j=(j-1)/2){
                        if(l[j]<l[(j-1)/2]){
                                trade(l[j],l[(j-1)/2]);
                        }else{
                                break;
                        }
                }
        }

        for(i=0,k=N-1;i<N;i++){
                trade(l[0],l[k]);
                k--;
                for(j=0;j*2+1<=k;){
                        if(j*2+2>k || l[j*2+1]<l[j*2+2]){
                                if(l[j]>l[j*2+1]){
                                        trade(l[j],l[j*2+1]);
                                        j=j*2+1;
                                }else{
                                        break;
                                }
                        }
                        else if(l[j]>l[j*2+2]){
                                trade(l[j],l[j*2+2]);
                                j=j*2+2;
                        }else{
                                break;
                        }
                }
        }

        return 0;

}

long search(long l[],int head,int tail,long ft){
        if(tail-head<0){
                while(l[tail]+ft>M)tail++;
                return l[tail]+ft;
        }

        int i=(tail+head)/2;


        if(l[i]+ft>M)
                return search(l,i+1,tail,ft);
        else
                return search(l,head,i-1,ft);
}


main(){
        int i,j,k;

        long N,N2;

        long l[1000+500500+1];
        long maxpt;

        while(1){
                scanf("%ld %ld",&N,&M);
                if(N==0)break;
                for(i=0;i<N;i++)
                        scanf("%ld",&l[i]);

                for(i=0,k=N;i<N;i++)
                        for(j=i;j<N;j++)
                                if(l[i]+l[j]<M)
                                        l[k++]=l[i]+l[j];
                l[k++]=0;
                N=k;

                sort(l,N);

                maxpt=0;
                for(i=0;i<N;i++){
                        j=search(l,i,N-1,l[i]);
                        if(maxpt<j)
                                maxpt=j;
                }

                printf("%ld\n",maxpt);

        }
        return 0;
}