[SCOI2005]互不侵犯 题解
题目地址:洛谷:【P1896】[SCOI2005]互不侵犯 – 洛谷、BZOJ:Problem 1087. — [SCOI2005]互不侵犯King
题目描述
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
输入输出格式
输入格式:
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
输出格式:
所得的方案数
输入输出样例
输入样例#1:
3 2
输出样例#1:
16
题解
听说是状压入门?我不知道,我的第一道状压并不是这个呀。
注意到n的范围极小,意味着可以状压来表示状态,但是状态数量很多,不能全搜一遍,考虑动态规划,只状压一行的状态。dp[i][S][j]表示第i行,状态为S且用去了j个国王的方案数,枚举上一行的状态转移过来即可。
判断是否冲突可以用左移一位、右移一位和原来的状态值去做与运算,若得到的结果非0,说明有冲突。
代码
// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(!isdigit(c)) {
if(c == '-') neg = -1;
c = fgc();
}
while(isdigit(c)) {
res = (res << 1) + (res << 3) + c - '0';
c = fgc();
}
return res * neg;
}
int n, k, cnt[1 << 9];
LL dp[10][1 << 9][85];
bool can[1 << 9];
int main() {
n = readint(); k = readint();
for(int i = 1; i < 1 << n; i++) {
cnt[i] = cnt[i >> 1] + (i & 1);
}
for(int i = 0; i < 1 << n; i++) {
if((i & (i << 1)) || (i & (i >> 1))) continue;
can[i] = true; dp[1][i][cnt[i]] = 1;
}
for(int i = 2; i <= n; i++) {
for(int s = 0; s < 1 << n; s++) {
if(!can[s]) continue;
for(int t = 0; t < 1 << n; t++) {
if(!can[t]) continue;
if((s & t) || (s & (t >> 1)) || (s & (t << 1))) continue;
for(int j = cnt[t]; j <= k; j++) {
dp[i][s][j + cnt[s]] += dp[i - 1][t][j];
}
}
}
}
LL ans = 0;
for(int i = 0; i < 1 << n; i++) {
ans += dp[n][i][k];
}
printf("%lld", ans);
return 0;
}