洛谷 P1896 [SCOI2005]互不侵犯
P1896 [SCOI2005]互不侵犯
状压DP(思路:预处理——>判相容——>状态转移——>输出)
原理回顾
二进制表示:
1.sit[j]表示国王情况(1有0无)
2.sta[j]表示国王个数
几个关键点
0.正确的预处理
void dfs(int x, int num, int cur) {//预处理每一个状态
if (cur >= n) { // 处理完毕
sit[++cnt] = x;
sta[cnt] = num;
return;//新建状态
}
dfs(x, num, cur + 1); // cur位置不放国王
dfs(x + (1 << cur), num + 1,cur + 2); // cur位置放国王,相邻位置不放
}
1. 状态转移方程
j是当前行国王情况sit[j],k是i-1行国王情况sit[k];第三维表示国王个数sta;累加
f[i][j][s]+=f[i-1][k][s-sta[j]];
2. 判断相邻
用位运算
sit[j]&sit[k](上下有重复的king)
(sit[j]<<1)&sit[k](左上右下有重复king)
sit[j]&(sit[k]<<1)(右上左下有重复king)
插播:位运算复习
& 按位与:同1为1
| 按位或:同0为0
^ 按位异或:同为0,异为1
~ 按位取反:0变1,1变0
num << i 左移:num*2^i(没超出范围时)
num >> i 右移:num/2^i(向下取整)
- n的2进制表示中第k位是几
- n>>k (所求移到最后一位)
- 看个位数 n&1
- lowbit(x):返回x的最后一位1(带上后缀0
- x&-x
- 原理:x&(~x+1)负数补码
- 应用:统计1的个数,树状数组
细节+易错点
1.不开long long 见祖宗
2.小心变量
完整代码
#include<iostream>
#include<algorithm>
using namespace std;
int sit[2000], sta[2000];
long long dp[100][2000][100];
int n,k, cnt = 0;
void dfs(int x, int num, int cur) {
if (cur >= n) {
sit[++cnt] = x;
sta[cnt] = num;
return;
}
dfs(x, num, cur + 1);
dfs(x + (1 << cur), num + 1, cur + 2);
}
bool hexie(int j, int k) {
if (sit[j] & sit[k] || (sit[j] << 1) & sit[k] || sit[j] & (sit[k] << 1))
return false;
return true;
}
int main() {
cin >> n>>k;
dfs(0, 0, 0);
for (int j = 1; j <= cnt; j++)
dp[1][j][sta[j]] = 1;
for (int i = 2; i <= n; i++)
for(int j=1;j<=cnt;j++)
for (int x = 1; x <= cnt; x++) {
if (!hexie(j, x))
continue;
for (int s = sta[j]; s <= k; s++)
dp[i][j][s] += dp[i - 1][x][s - sta[j]];
}
long long ans = 0;
for (int i = 1; i <= cnt; i++)
ans += dp[n][i][k];
cout << ans << endl;
return 0;
}