Problem:
ビンゴ

問題

あるプログラミングコンテストでは,競技後の懇親会でビンゴゲームをする習わしがある．しかし,このビンゴゲームで使うビンゴカードは少々特殊で,以下の条件に従って作成される．

ビンゴカードは N 行 N 列のマス目に区切られており，各マス目には正整数が１つずつ書かれている．それらの整数は全て異なる．

マス目に書かれている整数は 1 以上 M 以下である．

ビンゴカードに書かれているN×N個の整数の合計は S である．

どの列を見たときも，上から下に向かって整数は昇順に並んでいる．

どのマス目の整数も，そのマス目より左の列のどの整数よりも大きい．

以下は， N = 5, M = 50, S = 685 のときのビンゴカードの例である．

懇親会のために上の条件を満たすビンゴカードをできるだけたくさん作りたい．ただし，同一のカードを2枚以上作ってはならない．作ることができるビンゴカードの枚数の最大値を 100000 で割った余りを出力するプログラムを作成せよ．

入力

入力は複数のデータセットからなる．各データセットは以下の形式で与えられる．

入力は1行からなり, その行にはビンゴカードのサイズ N (1≤N≤7), マス目に書かれている整数の上限 M (1≤M≤2000),ビンゴカードに書かれている整数の合計 S (1≤S≤3000) を表す3つの正整数が空白区切りで書かれている．ただし, 与えられるどの入力データにおいても，条件を満たすビンゴカードを1枚以上作ることができる．

N, M, S が 0 のとき入力の終了を示す. データセットの数は 5 を超えない．

出力

データセットごとに, 作ることができるビンゴカードの枚数の最大値を 100000 で割った余りを1行に出力せよ．

入出力例

入力例

3 9 45
3 100 50
5 50 685
0 0 0

出力例

1
7
74501

3つ目の入力例に対して，作ることができるビンゴカードの枚数の最大値は 642499974501 であるので， 100000 で割った余りの 74501 を出力する．

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

Solution in JAVA (judge verdict: Memory Limit Exceeded)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;

public class Main {

	public static final int MAX_N = 7;
	public static final int MAX_D = MAX_N * MAX_N;
	public static final int MAX_M = 2000;
	public static final int MAX_S = 3000;
	
	public static ArrayList<ArrayList<HashMap<Integer, Integer>>> memo = new ArrayList<ArrayList<HashMap<Integer,Integer>>>(MAX_D);
	
	//public static int[][][] memo = new int[MAX_D + 1][MAX_S + 1][MAX_M + 1];
	
	public static int DFS(int deep, int max_d, int S, int M, final int max_M){
		//System.out.println(deep + " " + max_d + " " + S + " " + M + " " + max_M);
		if(/*memo[deep][S][M] >= 0*/ memo.get(deep).get(S).containsKey(M)){
			//System.out.println(S + " " + M + " " + memo[deep][S][M]);
			return /*memo[deep][S][M]*/ memo.get(deep).get(S).get(M);
		} else if(deep == max_d){
			//System.out.println(S + " " + M);
			/*return memo[deep][S][M] = (S == 0 ? 1 : 0);*/ memo.get(deep).get(S).put(M, S == 0 ? 1 : 0);
			return S == 0 ? 1 : 0;
		}
		
		/* cut */
		final int diff = (max_d - deep);
		if(S < diff * M){
			//System.out.println(diff + " " + S + " " + M);
			memo.get(deep).get(S).put(M, 0);
			return 0;
			
			//return memo[deep][S][M] = 0;
		}
		
		int sum = 0;
		for(int next_M = M + 1; next_M <= max_M; next_M++){
			if(S < next_M){
				break;
			}
			
			sum += DFS(deep + 1, max_d, S - next_M, next_M, max_M);
			sum %= 100000;
		}
		
		//System.out.println(deep + " " + S + " " + M + " " + sum);
		
		memo.get(deep).get(S).put(M, sum);
		return sum;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		for(int i = 0; i <= MAX_D; i++){
			memo.add(new ArrayList<HashMap<Integer,Integer>>(MAX_S));
			
			for(int j = 0; j <= MAX_S; j++){
				memo.get(i).add(new HashMap<Integer, Integer>());
			}
		}
		
		while (true) {
			final int N = sc.nextInt();
			final int M = sc.nextInt();
			final int S = sc.nextInt();

			if (N == 0 && M == 0 && S == 0) {
				break;
			}
			
			
			for(int i = 0; i <= N * N; i++){
				for(int j = 0; j <= S; j++){
					memo.get(i).get(j).clear();
				}
			}
			
			
			System.out.println(DFS(0, N * N, S, 0, M));
		}
	}

}