AtCoderのARC    次のARCの問題へ    前のARCの問題へ

ARC008-C THE☆たこ焼き祭り2012


問題へのリンク


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("0 0 300 10");
            WillReturn.Add("0 100 10 100");
            WillReturn.Add("0 200 10 200");
            WillReturn.Add("0 300 10 300");
            //3
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("4");
            WillReturn.Add("0 0 100 10");
            WillReturn.Add("0 90 10 10");
            WillReturn.Add("0 100 30 100");
            WillReturn.Add("-20 100 10 10");
            //3
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("1");
            WillReturn.Add("0 0 3 3");
            //0
        }
        else if (InputPattern == "Input4") {
            WillReturn.Add("4");
            WillReturn.Add("58 -49 38 109");
            WillReturn.Add("45 -29 200 56");
            WillReturn.Add("-32 123 103 98");
            WillReturn.Add("49 -234 289 43");
            //4.874179
        }
        else if (InputPattern == "Input5") {
            WillReturn.Add("8");
            WillReturn.Add("100 100 30 50");
            WillReturn.Add("100 50 93 123");
            WillReturn.Add("100 0 89 111");
            WillReturn.Add("50 100 13 18");
            WillReturn.Add("50 0 155 86");
            WillReturn.Add("0 100 30 58");
            WillReturn.Add("0 50 58 49");
            WillReturn.Add("0 0 98 153");
            //7.666667
        }
        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 NodeInfoDef
    {
        internal long NodeID;
        internal long X;
        internal long Y;
        internal long T;
        internal long R;
    }
    static List<NodeInfoDef> mNodeInfoList = new List<NodeInfoDef>();

    struct EdgeInfoDef
    {
        internal long ToNode;
        internal double Cost;
    }
    static Dictionary<long, List<EdgeInfoDef>> mEdgeInfoListDict = new Dictionary<long, List<EdgeInfoDef>>();

    static void Main()
    {
        List<string> InputList = GetInputList();
        long N = long.Parse(InputList[0]);

        if (N == 1) {
            Console.WriteLine(0);
            return;
        }

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

        long CurrNodeID = 1;
        foreach (string EachStr in InputList.Skip(1)) {
            SplitAct(EachStr);
            NodeInfoDef WillAdd;
            WillAdd.NodeID = CurrNodeID++;
            WillAdd.X = wkArr[0];
            WillAdd.Y = wkArr[1];
            WillAdd.T = wkArr[2];
            WillAdd.R = wkArr[3];
            mNodeInfoList.Add(WillAdd);
        }

        // ノードのペアごとに、有向辺を設定
        foreach (NodeInfoDef EachNode1 in mNodeInfoList) {
            foreach (NodeInfoDef EachNode2 in mNodeInfoList) {
                long FromNode = EachNode1.NodeID;
                long ToNode = EachNode2.NodeID;
                double Cost = GetEdgeCost(EachNode1, EachNode2);

                if (mEdgeInfoListDict.ContainsKey(FromNode) == false) {
                    mEdgeInfoListDict[FromNode] = new List<EdgeInfoDef>();
                }
                EdgeInfoDef WillAdd;
                WillAdd.ToNode = ToNode;
                WillAdd.Cost = Cost;
                mEdgeInfoListDict[FromNode].Add(WillAdd);
            }
        }

        Dictionary<long, double> KakuteiNodeDict = Dijkstra(1);

        double[] ValuesArr = KakuteiNodeDict.Values.ToArray();
        ValuesArr = ValuesArr.OrderByDescending(pX => pX).ToArray();

        var AnswerList = new List<double>();
        double WaitTime = 0;
        for (long I = 0; I <= ValuesArr.GetUpperBound(0) - 1; I++) {
            AnswerList.Add(ValuesArr[I] + WaitTime);
            WaitTime++;
        }
        Console.WriteLine(AnswerList.Max());
    }

    // ノード1 → ノード2 の有向辺のコストを返す
    static double GetEdgeCost(NodeInfoDef pNode1, NodeInfoDef pNode2)
    {
        // 2点間の距離を求める
        double XDiff = pNode1.X - pNode2.X;
        double YDiff = pNode1.Y - pNode2.Y;
        double Distance = Math.Sqrt(XDiff * XDiff + YDiff * YDiff);

        // たこやきの速度
        double Speed = Math.Min(pNode1.T, pNode2.R);

        return Distance / Speed;
    }

    // ダイクストラ法で、各ノードまでの最短距離を求める
    static Dictionary<long, double> Dijkstra(long pStaNode)
    {
        var InsPQueue = new PQueue_Arr();

        // 距離合計[確定ノード]なDict
        var KakuteiNodeDict = new Dictionary<long, double>();
        KakuteiNodeDict.Add(pStaNode, 0);

        // Enqueue処理
        Action<long> EnqueueAct = pFromNode =>
        {
            if (mEdgeInfoListDict.ContainsKey(pFromNode) == false) {
                return;
            }
            foreach (EdgeInfoDef EachEdge in mEdgeInfoListDict[pFromNode]) {
                // 確定ノードならContinue
                if (KakuteiNodeDict.ContainsKey(EachEdge.ToNode)) continue;

                double wkSumCost = KakuteiNodeDict[pFromNode] + EachEdge.Cost;

                PQueue_Arr.PQueueJyoutaiDef WillEnqueue;
                WillEnqueue.Node = EachEdge.ToNode;
                WillEnqueue.SumCost = wkSumCost;
                InsPQueue.Enqueue(WillEnqueue);
            }
        };
        EnqueueAct(pStaNode);

        while (InsPQueue.IsEmpty() == false) {
            PQueue_Arr.PQueueJyoutaiDef Dequeued = InsPQueue.Dequeue();

            // 確定ノードならcontinue
            if (KakuteiNodeDict.ContainsKey(Dequeued.Node)) continue;

            KakuteiNodeDict.Add(Dequeued.Node, Dequeued.SumCost);
            EnqueueAct(Dequeued.Node);
        }

        return KakuteiNodeDict;
    }
}

#region PQueue_Arr
// 内部で配列使用の優先度付きキュー
internal class PQueue_Arr
{
    internal struct PQueueJyoutaiDef
    {
        internal long Node;
        internal double SumCost;
    }

    private PQueueJyoutaiDef[] mHeapArr;
    private long mHeapArrCnt = 0;

    // コンストラクタ
    internal PQueue_Arr()
    {
        mHeapArr = new PQueueJyoutaiDef[65535];
    }
    internal bool IsEmpty()
    {
        return mHeapArrCnt == 0;
    }

    // エンキュー処理
    internal void Enqueue(PQueueJyoutaiDef pAddJyoutai)
    {
        long CurrNode = 1 + mHeapArrCnt;
        if (mHeapArr.GetUpperBound(0) < CurrNode) {
            ExtendArr();
        }
        mHeapArr[CurrNode] = pAddJyoutai;
        mHeapArrCnt++;

        while (1 < CurrNode && mHeapArr[CurrNode / 2].SumCost > mHeapArr[CurrNode].SumCost) {
            PQueueJyoutaiDef Swap = mHeapArr[CurrNode];
            mHeapArr[CurrNode] = mHeapArr[CurrNode / 2];
            mHeapArr[CurrNode / 2] = Swap;

            CurrNode /= 2;
        }
    }

    // 配列のExtend
    private void ExtendArr()
    {
        PQueueJyoutaiDef[] NewHeapArr = new PQueueJyoutaiDef[mHeapArrCnt * 2];
        mHeapArr.CopyTo(NewHeapArr, 0);
        mHeapArr = NewHeapArr;
    }

    // デキュー処理
    internal PQueueJyoutaiDef Dequeue()
    {
        PQueueJyoutaiDef TopNode = mHeapArr[1];
        long LastNode = mHeapArrCnt;
        mHeapArr[1] = mHeapArr[LastNode];
        mHeapArrCnt--;

        MinHeapify(1);
        return TopNode;
    }

    // 根ノードを指定し、根から葉へヒープ構築
    private void MinHeapify(long pRootNode)
    {
        if (mHeapArrCnt <= 1) {
            return;
        }

        long Left = pRootNode * 2;
        long Right = pRootNode * 2 + 1;

        // 左の子、自分、右の子で値が最小のノードを選ぶ
        double Smallest = mHeapArr[pRootNode].SumCost;
        long SmallestNode = pRootNode;

        if (Left <= mHeapArrCnt && mHeapArr[Left].SumCost < Smallest) {
            Smallest = mHeapArr[Left].SumCost;
            SmallestNode = Left;
        }
        if (Right <= mHeapArrCnt && mHeapArr[Right].SumCost < Smallest) {
            Smallest = mHeapArr[Right].SumCost;
            SmallestNode = Right;
        }

        // 子ノードのほうが大きい場合
        if (SmallestNode != pRootNode) {
            PQueueJyoutaiDef Swap = mHeapArr[SmallestNode];
            mHeapArr[SmallestNode] = mHeapArr[pRootNode];
            mHeapArr[pRootNode] = Swap;

            // 再帰的に呼び出し
            MinHeapify(SmallestNode);
        }
    }
}
#endregion


解説

考察すると下記で解けると分かります。

辺にコストがある、有向グラフとして、
全部のノードペアごとに
移動にかかる時間を求めます。

負辺がありませんので、
ダイクストラ法で、あなたノードから
各参加者ノードまでの最小コストを求めることができます。

そして、一番時間のかかるノードから順に、
1秒のwaitを付けて、たこやきを配り、
最も時間のかかるノードが解となります。