Problem:
お菓子の分割
　

問題

長さ N ミリメートルの棒状のお菓子が 1 本ある(ここで N は偶数). 2 人の JOI
関係者が,このお菓子を複数本に切断して,合計 N/2 ミリメートルずつに分けること
にした.

理由は不明であるが,このお菓子は場所によって切断のしやすさが異なっている.
2 人は,お菓子を左から 1 ミリメートルごとに調べ,各場所で切断に何秒かかるか
を割り出した.2 人がお菓子を分けるための切断にかかる秒数の最小値を求めるプ
ログラムを作成せよ.

入力

入力の 1 行目には,棒の長さ N(2 ≤ N ≤ 10000 ,ただし N は偶数) が書かれてい
る.入力の i + 1 行目 (1 ≤ i ≤ N − 1) には,左端から i ミリメートル目の場所の切
断にかかる秒数を表す整数 ti (1 ≤ ti ≤ 10000) が書かれている.切断可能な場所が
N − 1 箇所であることに注意せよ.

採点用データのうち,配点の 5% 分については,高々 2 カ所を切断することで最
小値を実現でき,10% 分については,高々 3 カ所を切断することで最小値を実現で
きる.配点の 20% 分については,N ≤ 20 である.

出力

出力は2 人がお菓子を分るための切断にかかる秒数の最小値を含む 1 行からなる.

入出力例

入力例 1

6
1
8
12
6
2

出力例 1

7

この場合,左端から 1 ミリメートルと 4 ミリメートルの場所で切断するとかかる
秒数が最小となる.かかる秒数は 1 秒と 6 秒で,合計 7 秒である.

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

Solution in JAVA (judge verdict: Memory Limit Exceeded)

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;

public class Main {

	public static void main(String[] args) {
		IO io = new IO();
		int n = io.nextInt();
		int[] c = new int[n + 1];
		for (int i = 1; i < n; i++) {
			c[i] = io.nextInt();
		}
		c[n] = 0;
		int[][][] dp = new int[2][n + 1][n / 2 + 1];
		int[] min0 = new int[n / 2 + 1];
		int[] min1 = new int[n + 1];
		Arrays.fill(min0, 1 << 29);
		Arrays.fill(min1, 1 << 29);
		for (int i = 0; i <= n; i++) {
			for (int j = 0; j <= n / 2; j++) {
				for (int k = 0; k < 2; k++) {
					dp[k][i][j] = 1 << 29;
				}
			}
		}
		dp[1][0][0] = min1[0] = 0;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n / 2; j++) {
				if (j > i) {
					continue;
				}
				dp[0][i][j] = min1[i - j] + c[i];
				dp[1][i][j] = min0[j] + c[i];
				min0[j] = Math.min(min0[j], dp[0][i][j]);
				min1[i - j] = Math.min(min1[i - j], dp[1][i][j]);
				// System.out.println(i + "," + j + ":" + dp[i][j][0] + "/" +
				// dp[i][j][1] + "," + min[i][j][0] + "/" + min[i][j][1]);
			}
		}
		// System.out.println(Arrays.deepToString(dp));
		// System.out.println(Arrays.deepToString(min));
		int ans = 1 << 29;
		for (int i = 1; i <= n; i++) {
			ans = Math.min(ans, dp[0][i][n/2]);
			if (0 <= i - n / 2) {
				ans = Math.min(ans, dp[1][i][i-n/2]);
			}
		}
		System.out.println(ans);
	}

}

class IO {
	private final InputStream in;
	final PrintWriter out = new PrintWriter(System.out);
	private final byte[] buffer = new byte[1024];
	private int ptr = 0;
	private int buflen = 0;

	public IO() {
		this(System.in);
	}

	public IO(InputStream source) {
		this.in = source;
	}

	private boolean hasNextByte() {
		if (ptr < buflen) {
			return true;
		} else {
			ptr = 0;
			try {
				buflen = in.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (buflen <= 0) {
				return false;
			}
		}
		return true;
	}

	private int readByte() {
		if (hasNextByte())
			return buffer[ptr++];
		else
			return -1;
	}

	private static boolean isPrintableChar(int c) {
		return 33 <= c && c <= 126;
	}

	private static boolean isNewLine(int c) {
		return c == '\n' || c == '\r';
	}

	private void skipUnprintable() {
		while (hasNextByte() && !isPrintableChar(buffer[ptr]))
			ptr++;
	}

	private void skipNewLine() {
		while (hasNextByte() && isNewLine(buffer[ptr]))
			ptr++;
	}

	public boolean hasNext() {
		skipUnprintable();
		return hasNextByte();
	}

	public boolean hasNextLine() {
		skipNewLine();
		return hasNextByte();
	}

	public String next() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		StringBuilder sb = new StringBuilder();
		int b = readByte();
		while (isPrintableChar(b)) {
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	public char[] nextCharArray(int len) {
		char[] s = new char[len];
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		int i = 0;
		int b = readByte();
		while (isPrintableChar(b)) {
			if (i == len) {
				throw new InputMismatchException();
			}
			s[i++] = (char) b;
			b = readByte();
		}
		return s;
	}

	public String nextLine() {
		if (!hasNextLine()) {
			throw new NoSuchElementException();
		}
		StringBuilder sb = new StringBuilder();
		int b = readByte();
		while (!isNewLine(b)) {
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	public long nextLong() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		long n = 0;
		boolean minus = false;
		int b = readByte();
		if (b == '-') {
			minus = true;
			b = readByte();
		}
		if (b < '0' || '9' < b) {
			throw new NumberFormatException();
		}
		while (true) {
			if ('0' <= b && b <= '9') {
				n *= 10;
				n += b - '0';
			} else if (b == -1 || !isPrintableChar(b)) {
				return minus ? -n : n;
			} else {
				throw new NumberFormatException();
			}
			b = readByte();
		}
	}

	public int nextInt() {
		long nl = nextLong();
		if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) {
			throw new NumberFormatException();
		}
		return (int) nl;
	}

	public char nextChar() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		return (char) readByte();
	}

	public double nextDouble() {
		return Double.parseDouble(next());
	}

	public int[] arrayInt(int n) {
		int[] a = new int[n];
		for (int i = 0; i < n; i++)
			a[i] = nextInt();
		return a;
	}

	public long[] arrayLong(int n) {
		long[] a = new long[n];
		for (int i = 0; i < n; i++)
			a[i] = nextLong();
		return a;
	}

	public double[] arrayDouble(int n) {
		double[] a = new double[n];
		for (int i = 0; i < n; i++)
			a[i] = nextDouble();
		return a;
	}

	public void arrayInt(int[]... a) {
		for (int i = 0; i < a[0].length; i++)
			for (int j = 0; j < a.length; j++)
				a[j][i] = nextInt();
	}

	public int[][] matrixInt(int n, int m) {
		int[][] a = new int[n][m];
		for (int i = 0; i < n; i++)
			a[i] = arrayInt(m);
		return a;
	}

	public char[][] charMap(int n, int m) {
		char[][] a = new char[n][m];
		for (int i = 0; i < n; i++)
			a[i] = nextCharArray(m);
		return a;
	}
}