AtCoderのABC    次のABCの問題へ    前のABCの問題へ

ABC091-C 2D Plane 2N Points


問題へのリンク


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("3");
            WillReturn.Add("2 0");
            WillReturn.Add("3 1");
            WillReturn.Add("1 3");
            WillReturn.Add("4 2");
            WillReturn.Add("0 4");
            WillReturn.Add("5 5");
            //2
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("3");
            WillReturn.Add("0 0");
            WillReturn.Add("1 1");
            WillReturn.Add("5 2");
            WillReturn.Add("2 3");
            WillReturn.Add("3 4");
            WillReturn.Add("4 5");
            //2
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("2");
            WillReturn.Add("2 2");
            WillReturn.Add("3 3");
            WillReturn.Add("0 0");
            WillReturn.Add("1 1");
            //0
        }
        else if (InputPattern == "Input4") {
            WillReturn.Add("5");
            WillReturn.Add("0 0");
            WillReturn.Add("7 3");
            WillReturn.Add("2 2");
            WillReturn.Add("4 8");
            WillReturn.Add("1 6");
            WillReturn.Add("8 5");
            WillReturn.Add("6 9");
            WillReturn.Add("5 4");
            WillReturn.Add("9 1");
            WillReturn.Add("3 7");
            //5
        }
        else if (InputPattern == "Input5") {
            WillReturn.Add("5");
            WillReturn.Add("0 0");
            WillReturn.Add("1 1");
            WillReturn.Add("5 5");
            WillReturn.Add("6 6");
            WillReturn.Add("7 7");
            WillReturn.Add("2 2");
            WillReturn.Add("3 3");
            WillReturn.Add("4 4");
            WillReturn.Add("8 8");
            WillReturn.Add("9 9");
            //4
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static int mN;

    struct ABInfoDef
    {
        internal int A;
        internal int B;
    }
    static List<ABInfoDef> mABInfoList = new List<ABInfoDef>();

    struct CDInfoDef
    {
        internal int C;
        internal int D;
    }
    static List<CDInfoDef> mCDInfoList = new List<CDInfoDef>();

    static int mX; // Xの頂点数
    static int mY; // Yの頂点数
    static int mSourceNode; //ソースのノード
    static int mSinkNode;   // シンクのノード
    static int UB;

    // 隣接行列で枝を表現
    static int[,] mCapacityArr;
    static int[,] mFlowArr;

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

        int[] wkArr = { };
        Action<string> SplitAct = pStr =>
            wkArr = pStr.Split(' ').Select(pX => int.Parse(pX)).ToArray();

        foreach (string EachStr in InputList.Skip(1).Take(mN)) {
            SplitAct(EachStr);
            ABInfoDef WillAdd;
            WillAdd.A = wkArr[0];
            WillAdd.B = wkArr[1];
            mABInfoList.Add(WillAdd);
        }

        foreach (string EachStr in InputList.Skip(1 + mN)) {
            SplitAct(EachStr);
            CDInfoDef WillAdd;
            WillAdd.C = wkArr[0];
            WillAdd.D = wkArr[1];
            mCDInfoList.Add(WillAdd);
        }
        // OrderBy X座標 descでソートしておく
        mCDInfoList = mCDInfoList.OrderByDescending(pX => pX.C).ToList();

        // 2部マッチングで解く
        mX = mN;
        mY = mN;
        mSourceNode = mX + mY;
        mSinkNode = mSourceNode + 1;
        UB = mSinkNode;

        mCapacityArr = new int[UB + 1, UB + 1];
        mFlowArr = new int[UB + 1, UB + 1];

        // グラフに枝を追加する
        for (int I = 0; I <= mABInfoList.Count - 1; I++) {
            for (int J = 0; J <= mCDInfoList.Count - 1; J++) {
                // 赤い点のX座標 >= 青い点のX座標 なら Break
                if (mABInfoList[I].A >= mCDInfoList[J].C) {
                    break;
                }

                // 赤い点のY座標 < 青い点のY座標 なら マッチング可能
                if (mABInfoList[I].B < mCDInfoList[J].D) {
                    mCapacityArr[I, mX + J] = 1;
                }
            }
        }

        for (int I = 0; I <= mX - 1; I++) {
            mCapacityArr[mSourceNode, I] = 1;
        }
        for (int I = mX; I <= mX + mY - 1; I++) {
            mCapacityArr[I, mSinkNode] = 1;
        }

        // エドモンズ・カープで解く
        Solve();
    }

    static void Solve()
    {
        while (true) {
            List<int> NodeList = ExecBFS();
            if (NodeList == null) break;

            //Console.WriteLine("経路を発見しました");
            //NodeList.ForEach(pX => Console.Write("{0},", pX));
            //Console.WriteLine();

            // 経路に流す量
            int CurrFlow = int.MaxValue;

            for (int I = 0; I <= NodeList.Count - 2; I++) {
                int FromNode = NodeList[I];
                int ToNode = NodeList[I + 1];

                CurrFlow = Math.Min(CurrFlow, mCapacityArr[FromNode, ToNode]);
            }
            //Console.WriteLine("この経路に{0}の水を流します", CurrFlow);

            for (int I = 0; I <= NodeList.Count - 2; I++) {
                int FromNode = NodeList[I];
                int ToNode = NodeList[I + 1];

                mCapacityArr[FromNode, ToNode] -= CurrFlow;
                mFlowArr[FromNode, ToNode] += CurrFlow;

                // 逆辺を追加する
                mCapacityArr[ToNode, FromNode] += CurrFlow;
            }
        }

        int Answer = 0;
        for (int I = 0; I <= UB; I++) {
            Answer += mFlowArr[I, UB];
        }
        Console.WriteLine(Answer);
    }

    struct JyoutaiDef
    {
        internal int CurrNode;
        internal List<int> NodeList;
    }

    // 幅優先探索を行い、始点から終点へのノードのListを返す
    // なければnullを返す
    static List<int> ExecBFS()
    {
        var Queue = new Queue<JyoutaiDef>();
        JyoutaiDef WillEnqueue;
        WillEnqueue.CurrNode = mSourceNode; // 始点のノードはmSourceNode
        WillEnqueue.NodeList = new List<int>();
        WillEnqueue.NodeList.Add(WillEnqueue.CurrNode);
        Queue.Enqueue(WillEnqueue);

        // BFSを繰り返すので、レベルの低い訪問を優先しても問題ない
        var VisitedSet = new HashSet<int>();

        while (Queue.Count > 0) {
            JyoutaiDef Dequeued = Queue.Dequeue();

            // 終点のノードはmSinkNode
            if (Dequeued.CurrNode == mSinkNode) {
                return Dequeued.NodeList;
            }

            for (int I = 0; I <= UB; I++) {
                int CurrCapacity = mCapacityArr[Dequeued.CurrNode, I];
                if (CurrCapacity == 0) continue;

                if (VisitedSet.Add(I) == false) continue;

                WillEnqueue.CurrNode = I;
                WillEnqueue.NodeList = new List<int>(Dequeued.NodeList) { I };
                Queue.Enqueue(WillEnqueue);
            }
        }
        return null;
    }
}


解説

二部マッチングの、エドモンズ・カープで解いてます。