Problem:
巨樹の刻み手

あなたは N 種類の道具を使って、目の前の巨樹を切り倒そうとしています。はじめは、樹の耐久力は D、あなたの経験値は 0 ですが、道具 i で１回樹を叩くと樹の耐久力は ai 減り、あなたは ei の経験値を得ます。ただし、道具 i を使うためには、あなたは ri 以上の経験値を持っていなければなりません。樹の耐久力が 0 以下になると樹は倒れます。

樹の耐久力と道具についての情報が与えられたとき、樹を切り倒すには最低何回樹を叩かなければいけないかを求めるプログラムを作成してください。

入力

入力は複数のデータセットからなる。入力の終わりはゼロ２つの行で示される。各データセットは以下の形式で与えられる。

D N
a1 e1 r1
a2 e2 r2
:
aN eN rN

1 行目に樹の耐久力を表す整数 D (1 ≤ D ≤ 100) と道具の種類の数 N(1 ≤ N ≤ 100) が与えられる。続く N 行に道具 1 から N までの情報が与えられる。ai (0 ≤ ai ≤ 100) と ei (0 ≤ ei ≤ 100) は、道具iを１回使うことで減る樹の耐久力とあなたが得ることのできる経験値をそれぞれ表す。ri (0 ≤ ri ≤ 100) は道具を使うために必要な経験値を表す。ai, ei, ri はすべて整数である。

データセットの数は 50 を超えない。

出力

樹を切り倒すのに必要な、樹を叩く最小の回数を１行に出力する。ただし、樹を切り倒すことが不可能な場合は NA と出力する。

入出力例

入力例

15 4
1 1 0
1 2 2
5 10 5
8 1 15
60 4
5 2 0
8 8 2
3 5 0
49 0 18
100 1
1 1 1
0 0

出力例

6
4
NA

Solution in C# (judge verdict: Accepted)

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

namespace _0288
{
    class Tool
    {
        public int Attack { get; set; }
        public int Exp { get; set; }
        public int Requirement { get; set; }
        public Tool(int[] vs)
        {
            Attack = vs[0];
            Exp = vs[1];
            Requirement = vs[2];
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                int[] dn = RArInt();
                if (dn.Sum() == 0) break;

                Tool[] tools = new Tool[dn[1]];
                for (int i = 0; i < dn[1]; i++) tools[i] = new Tool(RArInt());

                int[,] dp = new int[dn[0] + 1, 101];
                for (int i = 0; i < dp.GetLength(0); i++)
                {
                    for (int j = 0; j < dp.GetLength(1); j++)
                    {
                        dp[i, j] = int.MaxValue;

                    }
                }
                dp[0, 0] = 0;

                int maxI = dp.GetLength(0);
                int maxJ = dp.GetLength(1);

                for (int i = 0; i < maxI; i++)
                {
                    for (int j = 0; j < maxJ; j++)
                    {
                        if (dp[i, j] == int.MaxValue) continue;
                        foreach (var tool in tools)
                        {
                            if (j >= tool.Requirement)
                            {
                                int nextI = i + tool.Attack;
                                int nextJ = j + tool.Exp;
                                dp[Math.Min(nextI, maxI - 1), Math.Min(nextJ, maxJ - 1)] = Math.Min(dp[Math.Min(nextI, maxI - 1), Math.Min(nextJ, maxJ - 1)], dp[i, j] + 1);
                            }
                        }
                    }
                }

                int res = int.MaxValue;
                for (int i = 0; i < maxJ; i++)
                {
                    res = Math.Min(res, dp[maxI - 1, i]);
                }
                Console.WriteLine(res == int.MaxValue ? "NA" : res.ToString());

            }

        }
        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()); }
    }
}