Problem:
Score : 400 points

Problem Statement

Your friend gave you a dequeue D as a birthday present.

D is a horizontal cylinder that contains a row of N jewels.

The values of the jewels are V_1, V_2, ..., V_N from left to right. There may be jewels with negative values.

In the beginning, you have no jewel in your hands.

You can perform at most K operations on D, chosen from the following, at most K times (possibly zero):

Operation A: Take out the leftmost jewel contained in D and have it in your hand. You cannot do this operation when D is empty.

Operation B: Take out the rightmost jewel contained in D and have it in your hand. You cannot do this operation when D is empty.

Operation C: Choose a jewel in your hands and insert it to the left end of D. You cannot do this operation when you have no jewel in your hand.

Operation D: Choose a jewel in your hands and insert it to the right end of D. You cannot do this operation when you have no jewel in your hand.

Find the maximum possible sum of the values of jewels in your hands after the operations.

Constraints

All values in input are integers.

1 \leq N \leq 50

1 \leq K \leq 100

-10^7 \leq V_i \leq 10^7

Input

Input is given from Standard Input in the following format:

N K
V_1 V_2 ... V_N

Output

Print the maximum possible sum of the values of jewels in your hands after the operations.

Sample Input 1

6 4
-10 8 2 1 2 6

Sample Output 1

14

After the following sequence of operations, you have two jewels of values 8 and 6 in your hands for a total of 14, which is the maximum result.

Do operation A. You take out the jewel of value -10 from the left end of D.

Do operation B. You take out the jewel of value 6 from the right end of D.

Do operation A. You take out the jewel of value 8 from the left end of D.

Do operation D. You insert the jewel of value -10 to the right end of D.

Sample Input 2

6 4
-6 -100 50 -2 -5 -3

Sample Output 2

44

Sample Input 3

6 3
-6 -100 50 -2 -5 -3

Sample Output 3

0

It is optimal to do no operation.

Solution in C++14 (GCC 5.4.1) (judge verdict: Wrong Answer)

#include <bits/stdc++.h>

#define rep0(i, n) for(int i=0; i<(int)(n);i++)
#define rep1(i, n) for(int i=1; i<=(int)(n);i++)
#define all(x) (x).begin(), (x).end()
#define debug(a) if(flagdebug){cout << "debug (" #a "):" << endl << (a) << endl;}

using namespace std;

typedef uint64_t ulint;
typedef int64_t lint;

// #include <boost/multiprecision/cpp_int.hpp>
// using bint = boost::multiprecision::cpp_int;

template<class T> lint sum(vector<T> a);
template<class T> double ave(vector<T> a);
template<typename T> ostream& operator << (ostream& s, const vector<T>& v);
ulint gcd(lint a, lint b);
ulint lcm(lint a, lint b);

bool flagdebug = false;





int main(){
//////////////////////////////////////////////////////

    //flagdebug = true; ///本番時削除！！！！！

    //入力

    lint n, k;
    cin >> n >> k;

    vector<lint> v(n + 1,0);

    rep1(i, n)
    {
        cin >> v[i];
    }

    //　　 +---左から取る数--->
    //捨て |
    //る数 |   　価値
    //　　 v
    //lint lp = min(k,n); // 左からの操作回数は最大でもkかnの少ない方．
    vector<vector<lint>> dpleft(k + 1, vector<lint>(k + 1, 0));

    //  + ---左からの操作回数--->
    //       価値
    vector<lint> opeleft(k + k + 10, 0);

    for(lint i = 1; i <= k ; i ++)
    {
        if(i > k)
        {
            //宝石はこれ以上取れないので，ひたすらdp表をコピー
            rep0(j , k + 1)
            {
                dpleft[j][i] = dpleft[j][i - 1];
                opeleft[j + i] = max(opeleft[j + i], dpleft[j][i]);
            }
        }
        if(v[i] >= 0) //0以上の場合は捨てることを考えなくていい．
        {
            rep0(j , k + 1)
            {
                dpleft[j][i] = dpleft[j][i - 1] + v[i]; //i番目の宝の価値を追加してdpしていく
                opeleft[j + i] = max(opeleft[j + i], dpleft[j][i]);
            }
        }
        else //捨てる必要があるので1段ずつ落ちる
        {
            dpleft[0][i] = dpleft[0][i - 1] + v[i]; //捨てない場合は価値が減っちゃう．．．
            opeleft[i] = max(opeleft[i], dpleft[0][i]);
            rep1(j , k)
            {
                dpleft[j][i] = max(dpleft[j][i - 1] + v[i], dpleft[j - 1][i - 1] );
                //捨てない場合は表の左から価値変化を伴って遷移，
                //捨てる場合は表の左上から価値変化を伴わず遷移
                opeleft[j + i] = max(opeleft[j + i], dpleft[j][i]);
            }
        }
    }

    //　　 +---右から取る数--->
    //捨て |
    //る数 |   　価値
    //　　 v
    //lint rp = min(k,n); // 右からの操作回数は最大でもkかnの少ない方．
    vector<vector<lint>> dpright(k + 1, vector<lint>(k + 1, 0));

    //  + ---右からの操作回数--->
    //       価値
    vector<lint> operight(k + k + 10, 0);

    for(lint i = 1; i <= k ; i ++)
    {
        lint g = n - i + 1; //取る宝石の番号（左から1,2,3...）, 右から取っていく
        if(g <= 0)
        {
            //宝石はこれ以上取れないので，ひたすらdp表をコピー
            rep0(j , k + 1)
            {
                dpright[j][i] = dpright[j][i - 1];
                operight[j + i] = max(operight[j + i], dpright[j][i]);
            }
        }
        else if(v[g] >= 0) //価値0以上の場合は捨てることを考えなくていい．
        {
            rep0(j , k + 1)
            {
                dpright[j][i] = dpright[j][i - 1] + v[g]; //g番目の宝の価値を追加してdpしていく
                operight[j + i] = max(operight[j + i], dpright[j][i]);
            }
        }
        else //捨てる必要があるので1段ずつ落ちる
        {
            dpright[0][i] = dpright[0][i - 1] + v[g]; //捨てない場合は価値が減っちゃう．．．
            operight[i] = max(operight[i], dpright[0][i]);
            rep1(j , k)
            {
                dpright[j][i] = max(dpright[j][i - 1] + v[g], dpright[j - 1][i - 1] );
                //捨てない場合は表の左から価値変化を伴って遷移，
                //捨てる場合は表の左上から価値変化を伴わず遷移
                operight[j + i] = max(operight[j + i], dpright[j][i]);
            }
        }
    }

    // debug(dpleft);
    // debug(dpright);

    // debug(opeleft);
    // debug(operight);

    lint ans = 0;
    for(lint i = 0 ; i <= k ; i ++)
    {
        //左から取る数をiとし，残りk-i回を右から取るものとする．
        //そのときの最大価値を求める．

        ans = max(ans, opeleft[i] + operight[k - i]);
        
    }

    cout << ans << endl;

    //dp表の変換
    //各表を2次元から1次元に変換する
    //取る回数+捨てる回数が同じグループの中で，



    // vector<lint> a(n);
    //2次元配列
    //vector<vector<lint>> mat(row, vector<lint>(column, 初期値));

    // rep0(i, n)
    // {
    //     cin >> a[i];
    // }

    //2次元配列
    //vector<vector<lint>> mat(row, vector<lint>(column, 初期値));
    //ペア
    //pair<lint,lint> p = make_pair(1,2);
    //vector<pair<lint,lint>> c(100010, pair<lint,lint>(0,0));
    //タプル
    //tuple<lint,lint,lint> t  = make_tuple(1,2,3);


    //昇順ソート
    //sort(all(a));
    //降順ソート
    //sort(all(a),greater<lint>());
    //特殊なソート（ラムダ式）
    //sort(all(mat), [&i](const vector<lint> &a, const vector<lint> &b){return a[i] < b[i];});
    // //[&i]は参照する変数，いらなければからの大括弧でよし．
    //ソート済集合（順序付き集合）
    //set<lint> st;
    //itr.insert(0);
    //auto itrmin = st.begin(); //最小値の次へアクセス
    //auto itrmax = st.end(); //最大値の次へアクセス（かならずデクリメントが必要）
    //itrmax --; //最大値はst.end()の1つ前
    //st.erase(0);
    //st.find;
    
    //////////////////////////////////////////////////////
    //キュー
    //queue<lint> que;
    //que.push(value);
    //que.front(); //先頭を参照
    //que.top(); //トップを参照（消されないので消したい場合は直後にpop()を使う）
    //que.pop(); //追い出す
    //que.empty(); //空なら1，あれば0
    //優先度付きキュー
    //priority_queue<lint> que;
    //キューにque.push()で入れると最大値順に並べられる．que.pop()で取り出すときは常に最大値が前にくる．
    //最小値から取り出したい時はpriority_queue< lint, std::vector<lint>, std::greater<lint> >
    //////////////////////////////////////////////////////
    //スタック
    //stack<lint> stk;
    //stk.push(value);
    //stk.top(); //先頭を参照（消されないので消したい場合は直後にpop()を使う）
    //stk.pop(); //トップを削除
    //stk.empty(); //空なら1,あれば0
    //stk.size(); //数
    //
    // sort(input.begin() , input.end());
    // uint64_t max = numeric_limits<uint64_t>::max();

//////////////////////////////////////////////////////
// Ctrl + Opt + N to make
    return 0;
}


//vectorprint用
template<typename T> ostream& operator << (ostream& s, const vector<T>& v) {
    s << "[\t";
    for (auto i = v.begin(); i < v.end() - 1; i++){s << *i << "\t";}
    s << *(v.end() - 1) << "\t]\n";
	return s;
}

// 最大公約数
ulint gcd(lint a, lint b){
    if(b!=0){return gcd(b, a % b);}
    return a;    
}

//最小公倍数
ulint lcm(lint a, lint b){
    return a / gcd(a, b) * b;
}