yukicoder No.1 道のショートカット

問題概要

  • 1 \sim NまでN個の町がある。
  • 一方通行の道がV本あり、k (1 \leq k \leq V) 番目の道は町S_kから町T_kをつないでいる。(町T_kから町S_kへは行けない)
  • S_k < T_kで閉路はない。
  • k番目の道を通るのにY_k円が必要でM_k単位時間かかる。
  • C円以内で町1から出発して町Nまで移動するルートのうち、最も時間が短くなる方法で移動するとしたときの時間を求める。
  • そのようなルートがない場合は-1を出力。

制約

  • N (2 \leq N \leq 50)
  • C (1 \leq C \leq 300)
  • V (1 \leq V \leq 1500)
  • S_k (1 \leq S_k \leq N)
  • T_k (S_k < T_k \leq N)
  • Y_k (1 \leq Y_k \leq C)
  • M_k (1 \leq M_k \leq 1000)

解説

time_{現在いる町,使った金額} = かかった時間の最小値 として計算する。(DP)
DAGでS_k < T_kY_kも正なのでかかった金額がCを超えないようにループを回せばいい。
計算量はたぶんO(NCV)

ソースコード(C#)

#147241
テンプレート、入力は省略

public class Magatro
{
    private int N, C, V;
    private int[] S, T, Y, M;

    int Infinity = int.MaxValue;

    //iの町からGraph[i].Tの町までYのコストで時間M
    private List<Road>[] Graph;

    //[現在の町の位置,かかるコスト]
    private int[,] Time;

    public void Solve()
    {
        InitGraph();
        InitTime();
        Calc();
        Write();
    }

    //S,T,Y,MをGraphに入れる
    private void InitGraph()
    {
        Graph = new List<Road>[N];

        for (int i = 0; i < N; i++)
        {
            Graph[i] = new List<Road>();
        }

        for (int i = 0; i < V; i++)
        {
            Graph[S[i]-1].Add(new Road(T[i] - 1, Y[i], M[i]));
        }
    }

    //Timeの値をInfinityにして初期化
    private void InitTime()
    {
        Time = new int[N, C + 1];

        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j <= C; j++)
            {
                Time[i, j] = Infinity;
            }
        }
    }

    private void Calc()
    {
        Time[0, 0] = 0;

        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j <= C; j++)
            {
                if (Time[i, j] != Infinity)
                {
                    foreach (Road r in Graph[i])
                    {
                        //コストがC以下なら
                        if (j + r.Y <= C)
                        {
                            //Timeを更新
                            Time[r.T, j + r.Y] = Math.Min(Time[r.T, j + r.Y], Time[i, j] + r.M);
                        }
                    }
                }
            }
        }
    }

    private void Write()
    {
        int anser = int.MaxValue;
        bool flag = false;

        for(int i = 0; i <= C; i++)
        {
            if (Time[N - 1, i] != Infinity)
            {
                //flagを立ててanserを更新
                anser = Math.Min(anser, Time[N - 1, i]);
                flag = true;
            }
        }

        if (flag)
        {
            Console.WriteLine(anser);
        }
        else
        {
            //flagが立ってない。つまりNに行くルートがない
            Console.WriteLine(-1);
        }
    }
}

struct Road
{
    public int T, Y, M;

    public Road(int t,int y,int m)
    {
        T = t;
        Y = y;
        M = m;
    }
}

最後に

初めて解説をした。むずい
説明不足のところがあるかもしれない。(というより絶対にある)
これってDPでいいのかしら。
Calc( )のネストが深すぎるしTime[,]を更新するところももう少し上手く書く方法がある気がする。
というよりなんか読みにくい。
まだまだ実力が足りない。