Problem:
ケーキの切り分け２ (Cake 2)

JOI くんと IOI ちゃんは双子の兄妹である．JOI くんは最近お菓子作りに凝っていて，今日も JOI くんはケーキを焼いて食べようとしたのだが，焼きあがったところで匂いをかぎつけた IOI ちゃんが来たので 2人でケーキを分けることになった．

ケーキは円形である．ある点から放射状に切り目を入れ，ケーキを N 個のピースに切り分け，ピースに 1 から N まで反時計回りに番号をつけた．つまり，1 ≤ i ≤ N に対し，i 番目のピースは i − 1 番目と i + 1 番目のピースと隣接している (ただし 0 番目は N 番目，N + 1 番目は 1 番目とみなす) ．i 番目のピースの大きさは Ai だったが，切り方がとても下手だったので Ai はすべて異なる値になった．

図 1: ケーキの例 (N = 5, A1 = 2, A2 = 8, A3 = 1, A4 = 10, A5 = 9)

この N 個を JOI くんと IOI ちゃんで分けることにした．分け方は次のようにすることにした：

まず JOI くんが N 個のうちの好きな 1 つを選んで取る．

その後，IOI ちゃんからはじめて IOI ちゃんと JOI くんが交互に残りのピースを 1 つずつ取っていく．ただし，両隣のピースのうち少なくとも一方が既に取られているようなピースしか取ることができず，取れるピースが複数あるときは，IOI ちゃんはそのうち最も大きいものを選んで取り，JOI くんはそのうちで好きなものを選んで取ることができる．

JOI くんは，自分が最終的に取るピースの大きさの合計を最大化したい．

課題

ケーキのピースの数 N と，N 個のピースの大きさの情報が与えられたとき，JOI くんが取れるピースの大きさの合計の最大値を求めるプログラムを作成せよ．

入力

標準入力から以下の入力を読み込め．

1 行目には整数 N が書かれており，ケーキが N 個のピースに切り分けられていることを表す．

続く N 行のうちの i 行目 (1 ≤ i ≤ N) には整数 Ai が書かれており，i 番目のピースの大きさが Ai であることを表す．

出力

標準出力に，JOI くんが取れるピースの大きさの合計の最大値を表す整数を 1 行で出力せよ.

制限

すべての入力データは以下の条件を満たす．

1 ≤ N ≤ 2 000．

1 ≤ Ai ≤ 1 000 000 000．

Ai はすべて異なる．

入出力例

入力例 1

5
2
8
1
10
9

出力例 1

18

JOI くんは，次のようにピースを取るのが最適である．

JOI くんは 2 番目のピースを取る．このピースの大きさは 8 である．

IOI ちゃんは 1 番目のピースを取る．このピースの大きさは 2 である．

JOI くんは 5 番目のピースを取る．このピースの大きさは 9 である．

IOI ちゃんは 4 番目のピースを取る．このピースの大きさは 10 である．

JOI くんは 3 番目のピースを取る．このピースの大きさは 1 である．

最終的に，JOI くんが取ったピースの大きさの合計は，8 + 9 + 1 = 18 となる.

入力例 2

8
1
10
4
5
6
2
9
3

出力例 2

26

入力例 3

15
182243672
10074562
977552215
122668426
685444213
3784162
463324752
560071245
134465220
21447865
654556327
183481051
20041805
405079805
564327789

出力例 3

3600242976

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

Solution in C (judge verdict: Accepted)

#include <stdio.h>

// 定数定義
#define MAX_PIECE   (2000)

#define MAX(a,b)    ((a)>(b)?(a):(b))

// 汎用型宣言
typedef unsigned long long U8;
typedef unsigned long  U4;
typedef unsigned short U2;
typedef unsigned char  U1;

// 関数宣言
U8 dfs(U2 u2Lower, U2 u2Upper);

// グローバル変数（入力とメモ）
static U2 u2Num;
static U4 au4Size[MAX_PIECE * 2];

static U8 au8Memo[MAX_PIECE][MAX_PIECE] = {{0}}; // 初期化処理は省略

int main(){
    U2 u2Cnt;
    U8 u8Score;
    U8 u8Max;

    // 入力
    // ケーキのサイズは2週分のデータを格納して処理を簡略化する
    scanf("%hd",&u2Num);
    for(u2Cnt = 0; u2Cnt < u2Num; u2Cnt++){
        scanf("%ld",&au4Size[u2Cnt]);
        au4Size[u2Num + u2Cnt] = au4Size[u2Cnt];
    }

    // 初手は全探索 以降はメモ化再帰
    u8Max = 0;
    for(u2Cnt = 0; u2Cnt < u2Num; u2Cnt++){
        u8Score = au4Size[u2Cnt] + dfs(u2Cnt+1,u2Cnt+u2Num-1);
        u8Max = MAX(u8Max, u8Score);
    }

    // 出力
    printf("%lld\n", u8Max);

    return 0;
}


// 深さ優先探索（再帰）
// 残りのケーキの左右のインデックスを受け取り、そこから得られる最大点数を返す
U8 dfs(U2 u2Lower, U2 u2Upper){
    U8 u8Sum1;
    U8 u8Sum2;
    U2 u2NextLo;
    U2 u2NextUp;
    U8 u8Ans;

    // すでに計算済み
    if(au8Memo[u2Lower % u2Num][u2Upper % u2Num] > 0){
        return au8Memo[u2Lower % u2Num][u2Upper % u2Num];
    }

    // 残りケーキが0～1個だともう取れない
    if(u2Lower >= u2Upper){
        return 0;
    }

    // 相手が大きいほうを取る
    u2NextLo = u2Lower;
    u2NextUp = u2Upper;
    if(au4Size[u2NextLo] > au4Size[u2NextUp]){
        u2NextLo++;
    }else{
        u2NextUp--;
    }

    // 自分がどちらを取ればよいかは再帰して確認し結果が大きいほうを選ぶ
    u8Sum1 = au4Size[u2NextLo] + dfs(u2NextLo+1, u2NextUp);
    u8Sum2 = au4Size[u2NextUp] + dfs(u2NextLo, u2NextUp-1);
    u8Ans = MAX(u8Sum1, u8Sum2);

    // 計算結果を記憶
    au8Memo[u2Lower % u2Num][u2Upper % u2Num] = u8Ans;

    return u8Ans;
}