トップページに戻る    次の競技プログラミングの問題へ    前の競技プログラミングの問題へ

ABC-072-D Derangement

■■■問題■■■

1,2, ・・・ ,N からなる順列 p1,p2, ・・・ ,pN が与えられます。
次の操作を何回か (0回でもよい) 行うことが出来ます。

操作: 順列で隣り合う2つの数を選んでスワップする。

何回か操作を行って、任意の 1 <= i <= N に対して pi != i となるようにしたいです。
必要な操作の最小回数を求めてください。

■■■入力■■■

N
p1 p2  ・・・  pN

●2 <= N <= 10万
●p1,p2, ・・・ ,pN は 1,2, ・・・ ,N の順列である

■■■出力■■■

必要な操作の最小回数を出力せよ。


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("5");
            WillReturn.Add("1 4 3 5 2");
            //2
            //1と4を入れ替え、その後1と3を入れ替えることで p は 4,3,1,5,2 となり、
            //これは条件を満たします。
            //これが最小回数なので、答えは2となります。
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("2");
            WillReturn.Add("1 2");
            //1
            //1と2を入れ替えれば条件を満たします。
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("2");
            WillReturn.Add("2 1");
            //0
            //初めから条件を満たしています。
        }
        else if (InputPattern == "Input4") {
            WillReturn.Add("9");
            WillReturn.Add("1 2 4 9 5 8 7 3 6");
            //3
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static void Main()
    {
        List<string> InputList = GetInputList();
        int[] PArr = InputList[1].Split(' ').Select(X => int.Parse(X)).ToArray();

        int Answer = 0;
        for (int I = 0; I <= PArr.GetUpperBound(0); ) {
            if (PArr[I] != I + 1) {
                I++;
                continue;
            }

            Answer++;
            I += 2;
        }
        Console.WriteLine(Answer);
    }
}


解説

1 2 4 9 5 8 7 3 6 を完全順列にするのに必要な最小の交換回数は
2 1 4 9 8 5 3 7 6 のように交換すれば2回となります。

1 2 3 4 を完全順列にするのに必要な最小の交換回数は
2 1 4 3 のように交換すれば2回となります。

完全順列に反する要素をNG
完全順列を満たす要素をOK
と表すことにし、

要素を順に見ていき、完全順列に反する要素が登場したら、次の要素と交換すれば、
NG OKの場合でも、NG NGの場合でも
交換後は、OK OKとなります。

後は、上記の最適手順での交換回数を求めれば、解となります。