読者です 読者をやめる 読者になる 読者になる

ABC041 D: 徒競走

問題

D: 徒競走 - AtCoder Beginner Contest 041 | AtCoder
1〜Nの順列を考える。数 x_i は、数 y_i の前にあるという情報が M 個与えられる。条件を満たす順列の総数を求めよ。

  • 2 \le N \le 16
  • (x_i, y_i) のペアはすべて相異なる。
  • 全ての情報に合致する並びが少なくとも1つ存在する。

解法

全探索はO(N!)で間に合わないため、bitDPをして 2^N * N とする。

dp[使った要素の集合][最後に使った要素] := 順列の総数

として数え上げる。任意の使ってない要素に対して、最後に使った要素より先に来るような情報がある場合、そのような順列は数えてはならない。

またこの問題は、半順序関係に対応する全順序関係を数え上げる問題になるので、DAGについてトポロジカルソートの総数を求める問題といえる。以下のPDFの5ページ目が参考になる。http://www-erato.ist.hokudai.ac.jp/docs/autumn2013/inoue.pdf

ll dp[1<<16][16];
int N, M;
int G[16][16];
 
ll dfs(int S, int x) {
 
  if(S == (1<<N) - 1) return 1;
 
  ll& ret = dp[S][x == -1 ? 0 : x];
  if(~ret) return ret;
 
  ret = 0;
  rep(y, N)
    if(!(S >> y & 1)) {
      if(x == -1 || G[y][x] <= 0)
        ret += dfs(S | 1 << y, y);
      else
        return ret = 0;
    }
 
  return ret;
}
 
int main() {
 
  cin >> N >> M;
  minus(G);
  rep(i, M) {
    int x, y; cin >> x >> y;
    x--, y--;
    G[x][y] = 1;
    G[y][x] = 0;
  }
 
  minus(dp);
  cout << dfs(0, -1) << endl;
  
  return 0;
}