1087: [SCOI2005]互不侵犯King
Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4255 Solved: 2458 [Submit][Status][Discuss]Description
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上 左下右上右下八个方向上附近的各一个格子,共8个格子。
Input
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output
方案数。
Sample Input
3 2
Sample Output
16
暴力枚举铁定TLE,考虑状压
设$dp[i][j][k]$表示前$i$行共取了$j$个且第$i$行取的情况为$k$的合法方案数
然后预处理出每种取法的棋子数,以及两两之间是否可行,转移即可
#include <iostream> using namespace std; int n, k, Max; int cnt[512] = {0}; bool s1[512], s2[512][512]; void init(){ for(int i = 0; i <= Max; i++){ if(i & (i >> 1)){ s1[i] = false; continue; } s1[i] = true; for(int j = 0; j < 9; j++) if(i & (1 << j)) cnt[i]++; } for(int i = 0; i <= Max; i++) for(int j = i; j <= Max; j++) if((i & (j >> 1)) || (i & j) || ((i >> 1) & j)) s2[i][j] = s2[j][i] = false; else s2[i][j] = s2[j][i] = true; } long long dp[10][100][512] = {0}; int main(){ cin >> n >> k; Max = (1 << n) - 1; init(); for(int i = 0; i <= Max; i++) if(s1[i] && cnt[1] <= k) dp[1][cnt[i]][i] = 1; for(int i = 1; i < n; i++) for(int j = 0; j <= Max; j++) if(s1[j]) for(int l = 0; l <= Max; l++) if(s1[l] && s2[j][l]) for(int m = cnt[j]; m + cnt[l] <= k; m++) dp[i + 1][m + cnt[l]][l] += dp[i][m][j]; long long ans = 0; for(int i = 0; i <= Max; i++) if(s1[i]) ans += dp[n][k][i]; cout << ans << endl; return 0; }