car
好,这又是一道状压DP题。
题意:给你n * n的棋盘,有m个点不能放东西,要放k个车上去,问有多少种方案可以使它们不互相攻击。
n <= 18
然后就一眼状压了。
设f[i][j]表示前i行,已经放车的列状态是j的方案数。
然后有两种转移方案。我比较喜欢刷表法。
每次枚举i,然后枚举j,然后枚举i + 1行在哪列放车。
初始状态是f[0][0] = 1,最后统计f[n][x]
时间复杂度n²logn
1 #include <cstdio> 2 3 const int N = 20, MO = 1e9 + 7; 4 5 int f[N][1 << N]; 6 bool vis[N][N]; 7 8 inline bool check(int s, int k) { 9 int cnt = 0; 10 while(s) { 11 cnt += (s & 1); 12 if(cnt > k) { 13 return 0; 14 } 15 s >>= 1; 16 } 17 return cnt == k; 18 } 19 20 int main() { 21 //freopen("car.in", "r", stdin); 22 //freopen("car.out", "w", stdout); 23 int n, m, k; 24 scanf("%d%d%d", &n, &m, &k); 25 if(k > n) { 26 printf("0"); 27 return 0; 28 } 29 for(int i = 1, x, y; i <= m; i++) { 30 scanf("%d%d", &x, &y); 31 vis[x][y - 1] = 1; 32 } 33 34 f[0][0] = 1; 35 36 int lm = 1 << n; 37 for(int i = 0; i < n; i++) { 38 for(int j = 0; j < lm; j++) { 39 f[i + 1][j] += f[i][j]; 40 f[i + 1][j] %= MO; 41 for(int p = 0; p < n; p++) { 42 if(((j >> p) & 1) || vis[i + 1][p]) { 43 continue; 44 } 45 (f[i + 1][j | (1 << p)] += f[i][j]) %= MO; 46 } 47 } 48 } 49 50 int ans = 0; 51 for(int i = 0; i < lm; i++) { 52 if(check(i, k)) { 53 ans += f[n][i]; 54 ans %= MO; 55 } 56 } 57 printf("%d", ans); 58 return 0; 59 }