競技プログラミングの鉄則    次の問題へ    前の問題へ

A75 Examination


問題へのリンク


C#のソース

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

class Program
{
    static string InputPattern = "InputX";

    static List<string> GetInputList()
    {
        var WillReturn = new List<string>();

        if (InputPattern == "Input1") {
            WillReturn.Add("4");
            WillReturn.Add("20 70");
            WillReturn.Add("30 50");
            WillReturn.Add("30 100");
            WillReturn.Add("20 60");
            //4
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static long[] GetSplitArr(string pStr)
    {
        return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
    }

    struct TaskInfoDef
    {
        internal long T;
        internal long D;
    }
    static List<TaskInfoDef> mTaskInfoList = new List<TaskInfoDef>();

    static void Main()
    {
        List<string> InputList = GetInputList();

        long[] wkArr = { };
        Action<string> SplitAct = (pStr) => wkArr = GetSplitArr(pStr);

        foreach (string EachStr in InputList.Skip(1)) {
            SplitAct(EachStr);
            TaskInfoDef WillAdd;
            WillAdd.T = wkArr[0];
            WillAdd.D = wkArr[1];
            mTaskInfoList.Add(WillAdd);
        }

        // 締切の昇順にソート
        mTaskInfoList = mTaskInfoList.OrderBy(pX => pX.D).ToList();

        long UB = mTaskInfoList.Max(pX => pX.D);

        // こなしたタスク数[現在時刻]なインラインDP表
        long[] DPArr = new long[UB + 1];

        foreach (TaskInfoDef EachTaskInfo in mTaskInfoList) {
            for (long I = UB; 0 <= I; I--) {
                long NewI = I + EachTaskInfo.T;
                if (NewI > EachTaskInfo.D) {
                    continue;
                }

                long NewVal = DPArr[I] + 1;
                DPArr[NewI] = Math.Max(DPArr[NewI], NewVal);
            }
        }
        Console.WriteLine(DPArr.Max());
    }
}


解説

まず、処理順が不定だと計算量が多すぎるので、
こなすタスクの集合を決めた時に
全てのタスクをこなせるかを考えます。

タスクA かかる時間 8 締切 11
タスクB かかる時間 2 締切  6
という例で数直線で考えます。

0 1 2 3 4 5 6 7 8 9 10 11
      ------------------D
        ----D
Dは締切を表す。

すると、以下の制約で全てのタスクをこなす最適な方法を考えれば良いです。
●区間の横棒を、0からDの間で、Dを超えない範囲で横にスライド可能
●縦軸で見たときに複数タスクがあってはならない

これは、実際に区間の横棒を左右にスライドすることを考えれば、
Dが左にある区間から順に、
区間を配置していくのが最適だと直感的に分かります。

こなすタスクの集合を決めた時に
全てのタスクをこなすのに最適な順序が分かったので、
タスクを飛ばすことOKで、DPすれば解けます。

これは、こなしたタスク数[現在時刻]を更新する、インラインDPとなります。
遷移において、現在時刻は必ず増えるので、インラインDPにできます。