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

ABC187-F Close Group


問題へのリンク


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

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

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

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

        SplitAct(InputList[0]);
        int NodeCnt = wkArr[0];

        var EdgeHashSet = new HashSet<int>();

        foreach (string EachStr in InputList.Skip(1)) {
            SplitAct(EachStr);
            int A = wkArr[0];
            int B = wkArr[1];
            EdgeHashSet.Add(GetEdgeHash(A, B));
        }

        int AllBitON = (1 << NodeCnt) - 1;

        // 完全グラフを作れるノードの組み合わせのSet
        var OKBitSet = new HashSet<int>();

        for (int I = 1; I <= AllBitON; I++) {
            bool IsOK = true;
            for (int J = 0; J <= NodeCnt - 1; J++) {
                for (int K = J + 1; K <= NodeCnt - 1; K++) {
                    int Bit1 = 1 << J;
                    int Bit2 = 1 << K;
                    if ((Bit1 & I) == 0) continue;
                    if ((Bit2 & I) == 0) continue;
                    int Hash = GetEdgeHash(J + 1, K + 1);
                    if (EdgeHashSet.Contains(Hash) == false) {
                        IsOK = false;
                        break;
                    }
                }
                if (IsOK == false) break;
            }
            if (IsOK) {
                OKBitSet.Add(I);
            }
        }

        // 最小スコア[使用済のBitSet]なインラインDP表
        int[] DPArr = new int[AllBitON + 1];
        for (int I = 0; I <= AllBitON; I++) {
            if (OKBitSet.Contains(I)) {
                DPArr[I] = 1;
            }
            else {
                DPArr[I] = int.MaxValue;
            }
        }

        for (int I = 0; I <= AllBitON; I++) {
            foreach (int EachSubSet in DeriveEnumSubSet(I)) {
                int Bit1 = EachSubSet;
                int Bit2 = I - EachSubSet;
                if (DPArr[Bit1] == int.MaxValue) continue;
                if (DPArr[Bit2] == int.MaxValue) continue;
                DPArr[I] = Math.Min(DPArr[I], DPArr[Bit1] + DPArr[Bit2]);
            }
        }
        Console.WriteLine(DPArr[AllBitON]);
    }

    // 枝のハッシュ値を求める
    static int GetEdgeHash(int pVal1, int pVal2)
    {
        int Min = Math.Min(pVal1, pVal2);
        int Max = Math.Max(pVal1, pVal2);
        return Min * 100 + Max;
    }

    // 引数のBitSetの部分集合の列挙を返す
    static IEnumerable<int> DeriveEnumSubSet(int pBase)
    {
        int pCurr = pBase;
        while (true) {
            yield return pCurr;
            if (pCurr == 0) break;
            pCurr = (pCurr - 1) & pBase;
        }
    }
}


解説

最初に完全グラフになれるビットの組み合わせを列挙します。
次に2つの組み合わせごとに最小コストを求めるDPを行ってます。