状压DP
每一列对于每一行是唯一确定的,这一点契合了状态压缩的特性。
出题人的话:
一道状压 dp。首先不考虑皇后的差异性,把所有皇后当作是一样的,在第 i 行第 k 列安排一位皇后的方案数为
f[i][j|(1<<k)] += f[i][j]((j>>k)&1==0)
。因为皇后不可能是一样的,所以最后的结果为f[n][1<<(n-1)]*(n!)
。
#include <bits/stdc++.h> typedef long long ll; using namespace std; const int MOD = 1e9 + 7; const int maxn = 22; const int maxm = (1 << 21) + 10; int n; int g[maxn][maxn]; //图 int s[maxn][maxm]; // s[x][]表示有x个1的所有状态 int p[maxn]; //行游标 int dp[maxn][maxm]; // n行 m个状态 里有多少个合法情况 int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) scanf("%d", &g[i][j]); for (int i = 0; i < (1 << n); ++i) { //枚举所有长度为n的二进制串,表示一行的可选状态 int cnt = __builtin_popcount(i); // i里有多少个1 s[cnt][++p[cnt]] = i; // s[x][]表示x个1的所有状态 } dp[0][0] = 1; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { if (g[i][j]) continue; //不可选 for (int k = 1; k <= p[i - 1]; k++) { //枚举i-1个1的所有状态:之前选的所有状态 int pre = s[i - 1][k]; //前面几行可能的选择状态 if ((pre >> (j - 1)) & 1) continue; //不能有同一列 且 不可达此状态 这正是状压的可行性 int cur = pre | (1 << (j - 1)); //把从右往左第j位置1 dp[i][cur] = (dp[i][cur] + dp[i - 1][pre]) % MOD; // 当前状态 } } } ll fac = 1; for (ll i = 1; i <= n; i++) fac = fac * i % MOD; //全排列 printf("%lld\n", fac * dp[n][(1 << n) - 1] % MOD); return 0; }