C♯の勉強

C♯4.0 で TopCoder の過去問を解きます。

TopCoder SRM592: LittleElephantAndPermutationDiv1

LittleElephantAndPermutationDiv2 のように片方を(1,...N) に固定して考えるとその時点で解けなくなる。むずかしい。

a1 , .. , an
b1 , .. , bn

順列 a,b に対して、N から 1 の順、つまり大きいものから配置していくことを考える。
大きい順に配置するため、両方空いている場所に値 i を配置した時点で もう片側に i 以下の値しか入らないので、 magic(A,B) に i が追加される。

値の配置パターンを大きく以下の4通り。

「a,b 両方空いている場所」を2つ選んで値を配置

_ .... _  ->  N .... _
_ .... _  ->  _ .... N

上述の通り、空いている片側にはそれ以下の値しか入らないので、この時点で magic(A,B) に 2 * N 増える。
そして「a だけ空いている箇所」と「b だけ空いている箇所」が1つずつ増える

「a,b 両方空いている場所」を1つ選んで値を配置

_ ...  ->  N ....
_ ...  ->  N ....

magic(A,B) は N 増える。
「a だけ空いている箇所」と「b だけ空いている箇所」は特に変わらない

片方が空の場所と、両方とも空の場所を選んで配置

_ ... _  ->  N ... _
X ... _  ->  X ... N

すでに埋まっている値 X はより大きい値なので、magic(A,B) は N だけ増える。
「a だけ空いている箇所」と「b だけ空いている箇所」は特に変わらない

それぞれ片方が空の場所を選んで配置

_ ... X  ->  N ... X
X ... _  ->  X ... N

この場合は、magic(A,B) は増えない。
「a だけ空いている箇所」と「b だけ空いている箇所」は1ずつ減る。

ここで「a だけ空いている箇所」と「b だけ空いている箇所」の個数は同じになるので、後は
「何個配置したか」「a だけ空いている箇所」「magic(A,B)の値」で動的計画法を適用すればよい。

public class LittleElephantAndPermutationDiv1 {
    int mod = 1000000007;

    int?[, ,] memo = new int?[55, 2502, 55];

    int dfs(int N, int K, long single) {
        if (K < 0) K = 0;
        if (N == 0) {
            return K == 0 && single == 0 ? 1 : 0;
        }
        if (!memo[N, K, single].HasValue) {
            long val = 0;

            long empty = N - single;
            // 両方とも空いている場所を2つ選んで N を配置
            // _ .... _  ->  N .... _
            // _ .... _  ->  _ .... N
            if (empty >= 2) {
                val += empty * (empty - 1) * dfs(N - 1, K - 2 * N, single + 1);
            }

            // 両方とも空いている場所に1つ選んで N を配置
            // _ ...  ->  N ....
            // _ ...  ->  N ....
            if (empty >= 1) {
                val += empty * dfs(N - 1, K - N, single);
            }

            // 片方が空の場所と、両方とも空の場所を選んで配置
            // _ ... _  ->  N ... _
            // X ... _  ->  X ... N
            if (empty >= 1 && single >= 1) {
                val += empty * single * dfs(N - 1, K - N, single) * 2;
            }

            // それぞれ片方が空の場所を選んで配置
            // _ ... X  ->  N ... X
            // X ... _  ->  X ... N
            if (single >= 1) {
                val += single * single * dfs(N - 1, K, single - 1);
            }

            memo[N, K, single] = (int)(val % mod);
        }
        return memo[N, K, single].Value;
    }

    public int getNumber(int N, int K) {
        return dfs(N, K, 0);
    }
}