Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

パンケーキ

あなたが勤めているパンケーキ屋では、細長い鉄板にパンケーキの生地を横１列に並べて焼きます。パンケーキはへらで何回か裏返せば完成します。何回以上裏返せば完成するかはパンケーキごとに異なります。

へらは大きいので、隣り合ったパンケーキは２枚同時に裏返されてしまいます。このとき、これら２枚の位置は入れ替わりません。ただし、両端だけは、隣のパンケーキといっしょに裏返すだけでなく、１枚だけ裏返すこともできます。すべてのパンケーキを必要な回数以上裏返したら、全部いっぺんに鉄板からおろして完成です。

パンケーキを必要な回数より多く裏返すと固くなってしまうので、あまり多く裏返したくありません。そこであなたは、すべて完成するまでに、各パンケーキが裏返る回数の総和が最小になるような方法を見つけようと考えました。

鉄板の上のパンケーキの枚数と、完成するまでに何回以上裏返さなければならないかがパンケーキごとに与えられているとき、すべて完成するまでに各パンケーキが裏返る回数（へらを操作する回数ではない）の総和の最小値を計算するプログラムを作成せよ。

Input

入力は以下の形式で与えられる。

N
p1 p2 ... pN

１行目にパンケーキの枚数 N (3 ≤ N ≤ 5000)が与えられる。２行目に各パンケーキが完成するまでに必要な裏返す回数 pi (0 ≤ pi ≤ 3)が与えられる。

Output

すべて完成するまでに各パンケーキが裏返る回数の総和の最小値を１行に出力する。

Sample Input 1

3
1 2 1

Sample Output 1

4

へらを１回操作して左端と真ん中のパンケーキを裏返すと、２個のパンケーキが１回ずつ裏返るので、この操作で裏返る回数は２回。さらに、へらを１回操作して真ん中と右端のパンケーキを裏返すと、２個のパンケーキが１回ずつ裏返るので、この操作で裏返る回数は２回。以上の総和４回が答えになる。

Sample Input 2

3
0 3 0

Sample Output 2

6

へらを１回操作して左端と真ん中のパンケーキを裏返すと、この操作で裏返る回数は２回。これを３回繰り返したときの、総和の６回が答えになる（真ん中のパンケーキは、両隣のどちらかのパンケーキと一緒に裏返すことしかできないことに注意）。

Solution in C# (judge verdict: Accepted)

using System;
using System.Linq;
using System.Collections.Generic;


namespace PCK2016
{
    class Q6
    {
        static int N;
        static int[,,] dp;
        static int[] Datas;
        static void Main()
        {
            N = int.Parse(Console.ReadLine());
            dp = new int[N+2,10,10];
            var buff = Console.ReadLine().Split(' ').Select(x => int.Parse(x)).ToList();
            buff.Insert(0, 0);
            buff.Add(0);
            Datas = buff.ToArray();

            Console.WriteLine(dfs(N+1));
        }

        static int dfs(int cake, int hw1=0, int hw2=0)
        {
            if (cake == 0)
                return 0;

            if (hw1 >= Datas[cake - 1] && hw2 >= Datas[cake])
                return dfs(cake - 1, 0, hw1);

            if (hw2 < Datas[cake])
                if (cake == 1 || cake == N+1)
                    return dfs(cake, hw1 + 1, hw2 + 1) + 1;
                else
                    return dfs(cake, hw1 + 1, hw2 + 1) + 2;

            if (dp[cake, hw1, hw2] > 0)
                return dp[cake, hw1, hw2];

            if (cake == 1 || cake == N+1)
                return dp[cake,hw1,hw2] = Math.Min(dfs(cake, hw1 + 1, hw2 + 1) + 1, dfs(cake - 1, 0, hw1));

            return dp[cake,hw1,hw2] = Math.Min(dfs(cake, hw1 + 1, hw2 + 1) + 2, dfs(cake - 1, 0, hw1));
        }
    }
}