Problem:
ばらばら饅頭

会津若松市にある和菓子屋・友栗堂の店長さんは、とても腕のいい職人さんなのにちょっと気分屋なのが玉にキズです。店長さんの作る饅頭はとても美味しいのですが、その時の気分によって大きさがまちまちになってしまいます。

見かねた店長さんの奥さんは、大きさと重さがばらばらな饅頭を袋に詰めて売り出すことを思いつきました。一定の重さになるように饅頭を袋に詰めて売れば、一つ一つの大きさはばらばらでも一定の値段で売り出すことが出来ますし、どんな饅頭が入っているのか開けるまでわからないというサプライズも売りになるかもしれません。
「ばらばら饅頭」という名前で売りだしたところ、新しいアイディアが話題となり友栗堂はたちまち評判となりました。しかし、問題もあり、袋に入れる饅頭の組み合わせ方によって作れる「ばらばら饅頭」の数が変わり、無駄が発生してしまうことがありました。アルバイトで友栗堂に来ていたあなたは無駄なく袋に詰めるためにプログラムをつくることにしました。

饅頭の重さは1から9の9種類あります。袋に詰める時には、饅頭の重さの合計がぴったり10になるように詰め込みます。饅頭の組み合わせ方は1+1+2+4+2=10や1+1+1+1+1+5=10などで、同じ重さの饅頭をいくつ使っても構いません。

作られる饅頭の個数と饅頭の重さの情報を入力とし、作ることのできる「ばらばら饅頭」の最大の数を出力するプログラムを作成してください。

入力

複数のデータセットが与えられます。入力の終わりはゼロひとつで示されます。各データセットは以下の形式で与えられます。

n
m1 m2 ... mn

1行目に饅頭の個数 n (2 ≤ n ≤ 100) が与えられます。
2行目に各饅頭の重さmi (1 ≤ mi ≤ 9) が与えられます。

データセットの数は 100 を超えません。

出力

データセットごとに、詰め込むことのできた「ばらばら饅頭」の最大の数を1行に出力します。

入力例

5
4 9 1 3 8
10
8 5 3 6 2 1 4 5 4 5
9
5 7 3 8 2 9 6 4 1
0

出力例

1
4
4

Solution in JAVA (judge verdict: Accepted)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Scanner;

public class Main {
	
	public static int[][] col = 
		{
			{1,1,1,1,1,1,1,1,1,1},
			{2,1,1,1,1,1,1,1,1},
			{2,2,1,1,1,1,1,1},
			{2,2,2,1,1,1,1},
			{2,2,2,2,1,1},
			{2,2,2,2,2},
			{3,1,1,1,1,1,1,1},
			{3,2,1,1,1,1,1},
			{3,2,2,1,1,1},
			{3,2,2,2,1},
			{3,3,1,1,1,1},
			{3,3,2,1,1},
			{3,3,2,2},
			{3,3,3,1},
			{4,1,1,1,1,1,1},
			{4,2,1,1,1,1},
			{4,2,2,1,1},
			{4,2,2,2},
			{4,3,1,1,1},
			{4,3,2,1},
			{4,3,3},
			{4,4,1,1},
			{4,4,2},
			{5,1,1,1,1,1},
			{5,2,1,1,1},
			{5,2,2,1},
			{5,3,1,1},
			{5,3,2},
			{5,4,1},
			{6,1,1,1,1},
			{6,2,1,1},
			{6,2,2},
			{6,3,1},
			{7,1,1,1},
			{7,2,1},
			{8,1,1}
		};
	
	//public static int def_max = 0;
	
	public static long bit_trans(int[] weight){
		long bit = 0;
		
		for(int i = 1; i < 10; i++){
			bit <<= 7;
			bit |= weight[i];
		}
		
		return bit;
	}
	
	public static int dps(int[] weight, Map<Long, Integer> memo, int sum){
		long bit = bit_trans(weight);
		
		if(memo.containsKey(bit)){
			return memo.get(bit);
		}
		
		int max = 0;
		for(int i = 0; i < col.length; i++){
			boolean ok = true;
			
			for(int cost : col[i]){
				weight[cost]--;
				sum -= cost;
				
				if(weight[cost] < 0){
					ok = false;
				}
			}
			
			if(ok){
				max = Math.max(max, dps(weight, memo, sum) + 1);
				//def_max = Math.max(max, def_max);
			}
			
			for(int cost : col[i]){
				weight[cost]++;
				sum += cost;
			}
		}
		
		memo.put(bit, max);
		
		return max;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		Map<Long, Integer> map = new HashMap<Long, Integer>();
		
		while (true) {
			
			final int n = sc.nextInt();
			
			if(n == 0){
				break;
			}
			
			//def_max = 0;
			
			int[] weights = new int[10];
			
			for(int i = 0; i < n; i++){
				weights[sc.nextInt()]++;
			}
			
			int count = 0;
			
			count += weights[5] / 2;
			weights[5] %= 2;
			for(int i = 1; i < 5; i++){
				int min = Math.min(weights[5 + i], weights[5 - i]);
				
				count += min;
				weights[5 + i] -= min;
				weights[5 - i] -= min;
			}
			
			int sum = 0;
			for(int i = 1; i < weights.length; i++){
				sum += weights[i] * i;
			}
			
			//System.out.println(Arrays.toString(weights));
			System.out.println(count + dps(weights, map, sum));
			
		}
	}

}