Problem:
>土地分割

ハウジングメーカーの山田ハウスは、新たな目玉商品として、学校や病院など環境が充実している緑ホームタウンの分譲地を売り出しました。この分譲地は複数の区画に分割されており、好きなだけ購入できますが、購入する区画を合わせた土地の形状は長方形(正方形を含む)でなければなりません。

山田ハウスは、全区画が完売した分譲地を管理するため購入者ごとに境界線を引き、その区画の 1つに購入者番号を書いた看板を設置しておきました。境界線は地面に木の枝で線を引いただけのものであったため、数日後の豪雨により消えてしまい看板だけが残りました。図 1 は看板のあった区画に、その区画を買った購入者番号を記したものです。これでは分譲地がどのように買われたか分かりません。救いは事務所の引き出しにあった購入者番号 b と購入区画数 k のメモ(図 2)が見つかったことです。

図１（左）：看板の配置、図2（右）：メモ

プログラマーであるあなたは、プログラムを書いて山田ハウスを助けることになりました。分譲地の大きさ X × Y 、購入者数 n 、メモの情報 b、 k 、及び看板の位置情報 s を入力とし、図 3 に示すように各区画の購入者を出力するプログラムを作成してください。

図3：各区画の購入者

与えられた情報について、以下の場合は NA と出力します。

区画を区別する方法が存在しない場合

区画を区別する方法が複数存在する場合

Input

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

X Y n
b1 k1
b2 k2
:
bn kn
s11 s21 ... sX1
s12 s22 ... sX2
:
s1Y s2Y ... sXY

1 行目にX, Y, n (1 ≤ X, Y ≤ 10, 1 ≤ n ≤ 15) が与えられます。続く n 行にメモの i 行目に書かれた情報 bi (1 ≤ bi ≤ n), ki (1 ≤ ki ≤ 100) が与えられます。

続く Y 行に区画情報の i 行目の情報 sij が与えられます。sij はi 行目の左から j 番目の区画の看板情報を表します。看板の情報 sij として、その区画に看板が無い場合は 0 、看板がある場合はその区画の購入者番号が与えられます。

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

Output

入力データセットごとに、購入者情報またはNA を出力します。

Sample Input

5 4 6
1 6
2 4
3 3
4 4
5 1
6 2
0 0 1 0 0
0 0 0 2 0
0 0 0 3 0
0 4 5 0 6
3 3 1
1 9
0 0 1
0 0 0
0 0 0
4 4 4
1 6
2 2
3 4
4 4
0 1 0 0
0 0 0 2
0 0 3 0
0 4 0 0
0 0 0

Output for the Sample Input

1 1 1 2 2
1 1 1 2 2
4 4 3 3 3
4 4 5 6 6
1 1 1
1 1 1
1 1 1
NA

Solution in C# (judge verdict: Accepted)

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

namespace _0213
{
    public class Program

    {
        public class Customer
        {
            public int ID { get; set; }
            public int Area { get; set; }
            public Point DefaultP { get; set; }

            public Customer(int[] vs)
            {
                ID = vs[0];
                Area = vs[1];
            }
        }

        public class Point
        {
            public int I { get; set; }
            public int J { get; set; }
            public Point(int i, int j) { this.I = i; J = j; }
        }

        private static int[,] map;
        private static List<Customer> customers;
        private static int[,] ansMap;
        private static int ansCnt;

        public static void Main(string[] args)
        {
            while (true)
            {
                int[] xyn = RIntAr();
                if (xyn.Sum() == 0) break;
                Init(xyn);
                SetLot(0);
                Print();
            }
        }

        private static void Init(int[] xyn)
        {
            ansCnt = 0;
            ansMap = new int[xyn[1], xyn[0]];
            map = new int[xyn[1], xyn[0]];
            customers = new List<Customer>(xyn[2]);
            for (int i = 0; i < xyn[2]; i++)
            {
                customers.Add(new Customer(RIntAr()));
            }

            for (int i = 0; i < map.GetLength(0); i++)
            {
                int[] vs = RIntAr();
                for (int j = 0; j < map.GetLength(1); j++)
                {
                    map[i, j] = vs[j];
                    if (vs[j] > 0)
                    {
                        Customer ctm = customers.Where(x => x.ID == vs[j]).First();
                        ctm.DefaultP = new Point(i, j);
                    }
                }
            }
        }

        private static bool SetLot(int k)
        {
            if (k == customers.Count())
            {
                ansCnt++;
                if (ansCnt == 1)
                {
                    // １つ目の答えでは、答えを保持して処理継続
                    Array.Copy(map, ansMap, map.Length);
                    return false;
                }
                else
                {
                    // 答えが複数あれば打ち切り
                    return true;
                }
            }
            else
            {
                foreach (int width in GetDivisor(customers[k].Area).OrderBy(x => x))
                {
                    int height = customers[k].Area / width;
                    foreach (Point p in GetCandidateP(customers[k], height, width))
                    {
                        SetMap(customers[k], p, height, width);
                        if (SetLot(k + 1)) return true;
                        UnsetMap(customers[k], p, height, width);
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 整数の約数を列挙する(未ソート)
        /// </summary>
        /// <param name="n">整数</param>
        /// <returns>約数の列挙子</returns>
        public static IEnumerable<int> GetDivisor(int n)
        {
            int boundary = (int)Math.Floor(Math.Sqrt(n));
            for (int i = 1; i <= boundary; i++)
            {
                if (n % i == 0)
                {
                    yield return i;
                    if (i != n / i) yield return n / i;
                }
            }
        }

        private static IEnumerable<Point> GetCandidateP(Customer cmt, int height, int width)
        {
            if (map.GetLength(0) < height || map.GetLength(1) < width)
            {
                yield break;
            }

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int si = cmt.DefaultP.I - i;
                    int sj = cmt.DefaultP.J - j;
                    if (CanSet(cmt, si, sj, height, width)) yield return new Point(si, sj);
                }
            }
        }

        private static bool CanSet(Customer cmt, int si, int sj, int height, int width)
        {
            if (!IsInArea(si, sj) || !IsInArea(si + height - 1, sj + width - 1)) return false;

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if (map[si + i, sj + j] > 0 && map[si + i, sj + j] != cmt.ID) return false;
                }
            }
            return true;
        }

        private static bool IsInArea(int i, int j)
        {
            return 0 <= i && i < map.GetLength(0) && 0 <= j && j < map.GetLength(1);
        }

        private static void SetMap(Customer ctm, Point p, int height, int width, bool flg = true)
        {
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if (p.I + i == ctm.DefaultP.I && p.J + j == ctm.DefaultP.J) continue;
                    map[p.I + i, p.J + j] = flg ? ctm.ID : 0;
                }
            }
        }

        private static void UnsetMap(Customer ctm, Point p, int height, int width)
        {
            SetMap(ctm, p, height, width, false);
        }

        private static void Print()
        {
            if (ansCnt != 1)
            {
                Console.WriteLine("NA");
            }
            else
            {
                for (int i = 0; i < ansMap.GetLength(0); i++)
                {
                    for (int j = 0; j < ansMap.GetLength(1); j++)
                    {
                        if (j > 0) Console.Write(' ');
                        Console.Write(ansMap[i, j]);

                    }
                    Console.Write(Environment.NewLine);
                }
            }
        }

        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[] RStAr(char sep = ' ') { return Console.ReadLine().Trim().Split(sep); }
        static int[] RIntAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
        static long[] RLongAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => long.Parse(e)); }
        static double[] RDoubleAr(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()); }
    }

}