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 C# (judge verdict: Accepted)

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

namespace _0246
{
    class Program
    {

        static public int[] buns;
        static int res;
        static private List<int[]> ptn = new List<int[]>();

        static void Main(string[] args)
        {
            MakePtn(new List<long>(), 0);
            List<string> input = new List<string>();
            input = new List<string>();

            while (true)
            {

                int n = RInt();
                if (n == 0) break;
                int[] vs = RArInt();

                res = 0;
                buns = new int[10];
                for (int i = 0; i < vs.Length; i++) buns[vs[i]]++;

                res += GetTwoPairs();
                res += GetOtherPairs(buns, buns.Select((x, i) => i * buns[i]).Sum(), new Dictionary<ulong, int>());
                Console.WriteLine(res);
            }
        }

        private static void MakePtn(List<long> vs, long sum)
        {
            if (sum == 10)
            {
                int[] cnts = new int[10];
                for (int i = 0; i < vs.Count(); i++)
                {
                    cnts[vs[i]]++;
                }
                ptn.Add(cnts);
            }
            else
            {
                long s = vs.Count == 0 ? 1 : vs.Last();
                for (long i = s; i + sum <= 10; i++)
                {
                    if (i == 10) continue;
                    List<long> tn = new List<long>(vs); tn.Add(i);
                    MakePtn(new List<long>(tn), sum + i);
                }
            }
        }

        private static int GetTwoPairs()
        {
            int res = 0, cnt = 0;
            for (int i = 1; i < buns.Length / 2; i++)
            {
                cnt = Math.Min(buns[i], buns[buns.Length - i]);
                res += cnt;
                buns[i] -= cnt;
                buns[buns.Length - i] -= cnt;
            }
            res += buns[buns.Length / 2] / 2;
            buns[buns.Length / 2] = buns[buns.Length / 2] % 2;
            return res;
        }

        private static int GetOtherPairs(int[] rems, int sum, Dictionary<ulong, int> memo)
        {
            ulong bitRems = ChangeIArrToBit(rems);
            if (memo.ContainsKey(bitRems)) return memo[bitRems];

            int cnt = 0;
            for (int i = 0; i < ptn.Count(); i++)
            {
                int[] newRems = new int[rems.Length];
                Array.Copy(rems, newRems, rems.Length);
                if (RemoveRems(newRems, ptn[i]))
                {
                    cnt = Math.Max(cnt, 1 + GetOtherPairs(newRems, sum - 10, memo));
                }
            }
            memo.Add(bitRems, cnt);
            return cnt;
        }

        private static bool RemoveRems(int[] rems, int[] ptn)
        {
            for (int i = 0; i < ptn.Length; i++)
            {
                rems[i] -= ptn[i];
                if (rems[i] < 0) return false;
            }
            return true;
        }

        private static ulong ChangeIArrToBit(int[] cnts)
        {
            ulong res = 0;
            for (int i = 0; i < cnts.Length; i++)
            {
                res |= (ulong)cnts[i] << (i * 7);
            }
            return res;
        }

        static string RSt() { return Console.ReadLine(); }
        static int RInt() { return int.Parse(Console.ReadLine().Trim()); }
        static long RLong() { return long.Parse(Console.ReadLine().Trim()); }
        static double RDouble() { return double.Parse(Console.ReadLine()); }
        static string[] RArSt(char sep = ' ') { return Console.ReadLine().Trim().Split(sep); }
        static int[] RArInt(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
        static long[] RArLong(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => long.Parse(e)); }
        static double[] RArDouble(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => double.Parse(e)); }
        static string WAr<T>(IEnumerable<T> array, string sep = " ") { return string.Join(sep, array.Select(x => x.ToString()).ToArray()); }
    }
}