Problem:
Blur

図 1 のような 10×10 のマス目の「布」があり、（1，2）のようにX座標・Y座標の値の対でマス目を示すことにします。座標値は 0 から始まる整数とします。例えば、図 1 の◎の座標は (1, 2) です。
この「布」に染料を少しずつたらして染物を作ります。たらす染料の滴には「大」「中」「小」の 3 サイズがあり、染料の滴の落ちたマス目を中心に図 1 のように周囲も色がつきます。図 1 で☆が中心、○が色のにじむ範囲です。

「布」は、最初は「まっしろ」つまり、どのマス目も色の濃さを示す値が 0 とします。染料の滴が落ちるごとに値が 1 ずつ増えていきます。「小」が（1, 2）、「中」が（3, 2）に落ちた場合、各マス目の値は図 2 の左のようになります。染料がもったいないので、図 2 の右に示すようなにじむ範囲が布の外にはみ出すような染料の落とし方はしないことになっています。また、同じ場所に複数の染料をたらすこともあります。

このような作業を何回か繰り返した結果、布にすばらしい模様が浮かび上がりましたが、残念なことに、うっかり作業経過を記録し忘れてしまいました。どうにもさっぱり思い出せませんが、かろうじて、落とした染料の滴数は覚えていました。あなたは、すばらしい染物を再現しなければなりません。すばらしい染物のデータを読みこんで、どこに、どのような染料をたらしたかを出力するプログラムを作成してください。なお、落とした染料の滴数は 12 以下とします。

入力

入力の形式は以下のとおりです：

最初の 1 行に落とした染料の滴数 n が与えられます。次の行から 10 行に各座標の色の濃さが空白区切りで与えられます。

出力

出力は n 行からなります。染料の滴の「大」を 3、「中」を 2、「小」を 1 で表し、落とした染料ごとに、そのX座標、Y座標、滴の大きさを空白で区切って1行に出力してください。

染料のたらし方はどのような順番で出力しても良いものとします。

入力例1

2
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 0 1 1 1 0 0 0 0 0
0 0 0 1 0 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

出力例1

3 2 1
8 4 2

入力例2

6
0 0 1 0 0 0 0 0 0 0
0 1 1 1 0 0 0 0 0 0
1 1 1 1 1 1 1 1 0 0
0 1 1 1 1 1 1 1 0 0
0 0 1 1 1 3 1 1 0 0
0 0 1 1 3 1 1 1 0 0
0 0 1 1 1 1 1 1 1 0
0 0 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 0
0 0 0 0 0 0 0 1 0 0

出力例2

2 2 3
7 7 3
6 3 2
3 6 2
4 4 1
5 5 1

Solution in C# (judge verdict: Accepted)

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

namespace _0091

{
    public class Program

    {
        public enum DropSize { L = 0, M = 1, S = 2 }
        const int SIZE = 10;
        const int SENT = 2;
        static int[,] map = new int[SIZE + SENT * 2, SIZE + SENT * 2];

        static int[] sDx = new int[] { 0, -1, 1, 0, 0 };
        static int[] sDy = new int[] { 0, 0, 0, -1, 1 };
        static int[] mDx = new int[] { 0, -1, 1, 0, 0, -1, -1, 1, 1 };
        static int[] mDy = new int[] { 0, 0, 0, -1, 1, -1, 1, -1, 1 };
        static int[] lDx = new int[] { 0, -1, 1, 0, 0, -1, -1, 1, 1, -2, 2, 0, 0 };
        static int[] lDy = new int[] { 0, 0, 0, -1, 1, -1, 1, -1, 1, 0, 0, -2, 2 };

        static List<cand>[] cands = new List<cand>[3];
        static int[] candsCnt = new int[3];
        struct cand
        {
            public int I { get; set; }
            public int J { get; set; }
            public DropSize SIZE { get; set; }
            public cand(int i, int j, DropSize size) { I = i; J = j; SIZE = size; }
        }
        static Stack<cand> ans;

        public static void Main(string[] args)
        {
            int n = RInt();

            int mapSum = 0;
            for (int i = 0 ; i < SIZE ; i++)
            {
                int[] line = RIntAr();
                mapSum += line.Sum();
                for (int j = 0 ; j < SIZE ; j++) map[i + SENT, j + SENT] = line[j];
            }

            // 大・中・小の滴下数パターン候補
            List<int[]> dropsLMS = GetDropsCnts(n, mapSum);

            //滴下候補点の算出
            for (int i = 0 ; i < cands.Length ; i++) cands[i] = new List<cand>(SIZE * SIZE);
            for (int i = SENT ; i < SIZE + SENT ; i++)
            {
                for (int j = SENT ; j < SIZE + SENT ; j++)
                {
                    if (CanPut(i, j, DropSize.S))
                    {
                        cands[(int)DropSize.S].Add(new cand(i, j, DropSize.S));
                        candsCnt[(int)DropSize.S]++;
                        if (CanPut(i, j, DropSize.M))
                        {
                            cands[(int)DropSize.M].Add(new cand(i, j, DropSize.M));
                            candsCnt[(int)DropSize.M]++;
                            if (CanPut(i, j, DropSize.L))
                            {
                                cands[(int)DropSize.L].Add(new cand(i, j, DropSize.L));
                                candsCnt[(int)DropSize.L]++;
                            }
                        }
                    }
                }
            }

            int[,] defaultMap = new int[SIZE + SENT * 2, SIZE + SENT * 2];
            Array.Copy(map, defaultMap, map.Length);

            // 滴下数パターンごとに再帰して探索
            foreach (var dropsCmb in dropsLMS)
            {
                ans = new Stack<cand>(12);
                Array.Copy(defaultMap, map, map.Length);
                if (Solve(dropsCmb, 0, new int[] { 0, 0, 0 }, 0)) return;
            }
        }

        private static bool Solve(int[] dropsCmb, int sIdx, int[] Cnt, int k)
        {

            if (Cnt[(int)DropSize.L] == dropsCmb[(int)DropSize.L]
             && Cnt[(int)DropSize.M] == dropsCmb[(int)DropSize.M]
             && Cnt[(int)DropSize.S] == dropsCmb[(int)DropSize.S])
            {
                foreach (var item in ans) Console.WriteLine("{0} {1} {2}", item.J - SENT, item.I - SENT, 3 - (int)item.SIZE);
                return true;
            }

            if (dropsCmb[k] == 0) return Solve(dropsCmb, 0, Cnt, k + 1);
            if (k > (int)DropSize.S) return false;

            for (int i = sIdx ; i < cands[k].Count() ; i++)
            {
                if (CanPut(cands[k][i]))
                {
                    SetMap(cands[k][i]);
                    ans.Push(cands[k][i]);
                    Cnt[(int)cands[k][i].SIZE]++;

                    if (Cnt[k] >= dropsCmb[k])
                    {
                        if (Solve(dropsCmb, 0, Cnt, k + 1)) return true;
                    }
                    else
                    {
                        if (Solve(dropsCmb, i, Cnt, k)) return true;
                    }

                    Cnt[(int)cands[k][i].SIZE]--;
                    ans.Pop();
                    ReSetMap(cands[k][i]);
                }
            }
            return false;
        }

        private static void SetMap(cand cand)
        {
            int[] dj = GetDxOrDy(cand.SIZE, true);
            int[] di = GetDxOrDy(cand.SIZE, false);
            for (int i = 0 ; i < dj.Length ; i++) map[cand.I + di[i], cand.J + dj[i]]--;
        }
        private static void ReSetMap(cand cand)
        {
            int[] dj = GetDxOrDy(cand.SIZE, true);
            int[] di = GetDxOrDy(cand.SIZE, false);
            for (int i = 0 ; i < dj.Length ; i++) map[cand.I + di[i], cand.J + dj[i]]++;
        }

        private static bool CanPut(cand cand) { return CanPut(cand.I, cand.J, cand.SIZE); }
        private static bool CanPut(int i, int j, DropSize size)
        {
            int[] dj = GetDxOrDy(size, true);
            int[] di = GetDxOrDy(size, false);

            for (int k = 0 ; k < dj.Length ; k++) if (map[i + di[k], j + dj[k]] == 0) return false;
            return true;
        }

        private static int[] GetDxOrDy(DropSize size, bool isDj)
        {
            switch (size)
            {
                case DropSize.S: return isDj ? sDx : sDy;
                case DropSize.M: return isDj ? mDx : mDy;
                case DropSize.L: return isDj ? lDx : lDy; ;
            }
            return null;
        }

        private static List<int[]> GetDropsCnts(int n, int sum)
        {
            List<int[]> list = new List<int[]>();
            for (int i = 0 ; i <= n ; i++)
            {
                if (13 * i > sum) break;
                for (int j = 0 ; j <= n ; j++)
                {
                    if (13 * i + 9 * j > sum || i + j > n) break;
                    if (13 * i + 9 * j + 5 * (n - (i + j)) == sum) list.Add(new int[] { i, j, n - (i + j) });
                }
            }
            return list;
        }

        static int RInt() { return int.Parse(Console.ReadLine().Trim()); }
        static int[] RIntAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
    }

}