状压dp与普通dp不同的方面即为它把之前的各种状态用一个数字表示,通过这个数字二进制条件下各位是0还是1来表示这个状态下的各种情况(是否有放棋子之类的),然后通过枚举数字即可枚举之前的各种情况进行dp
位运算的技巧
1.判断一个数字x二进制下第i位是不是等于1。(最低第1位)
方法:if(((1<<(i−1))&x)>0) 将1左移i-1位,相当于制造了一个只有第i位 上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0, 说明x第i位上是1,反之则是0。
2.将一个数字x二进制下第i位更改成1。
方法:x=x|(1<<(i−1)) 证明方法与1类似。
3.将一个数字x二进制下第i位更改成0。
方法:x=x&~(1<<(i−1)) ~号代表按位全部取反
4.把一个数字二进制下最靠右的第一个1去掉。
方法:x=x&(x−1)
板子
https://www.luogu.com.cn/problem/P1896
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=160;
int f[11][maxn][maxn];
int num[maxn],s[maxn];
int n,k,cnt;
void init(){ //预处理一下没有其他行限制下每一行的可能情况有多少种
cnt=0;
for(int i=0;i<(1<<n);i++){
if(i&(i<<1)){ // 代表左右有相邻国王
continue;
}
int sum=0;
for(int j=0;j<n;j++){ //枚举一下i这个情况下哪些地方是国王
if(i&(1<<j)){
sum++;
}
}
s[++cnt]=i; //s[cnt]代表了第cnt种情况下的状态
num[cnt]=sum;
}
// cout<<"cnt "<<cnt<<"\n";
}
void solve(){
cin>>n>>k;
init();
f[0][1][0]=1; //代表第0行在num[1]即放了0个国王的情况有1种
for(int i=1;i<=n;i++){ //枚举行
for(int j=1;j<=cnt;j++){ //枚举这一行有多少种情况
for(int l=0;l<=k;l++){ //枚举算上这一行的国王总数
if(l>=num[j]){ //算上这一行放的国王总数起码得大于等于这一行自己就有的国王个数
for(int t=1;t<=cnt;t++){ //枚举上一行的情况
//1.不能跟上一行有列重合 2.不能刚好差一行
if(!(s[t]&s[j])&&!(s[t]&(s[j]<<1))&&!(s[t]&(s[j]>>1))){
f[i][j][l]+=f[i-1][t][l-num[j]];
}
}
}
}
}
}
int ans=0;
for(int i=1;i<=cnt;i++){
ans+=f[n][i][k];
}
cout<<ans<<"\n";
}
signed main(){
int t;
t=1;
while(t--){
solve();
}
}