状态压缩典型例子?c++
必定要说的一点:数组
\(\color{#fccefa}{不开long long一时爽,提交代码火葬场}\)spa
定义数组\(dp[i][j][s]\),code
表示当前要放第i行的国王blog
算上第i行放上的国王共有j个国王已经被放在棋盘上了get
第i行的状态是sit
那么能够考虑转移:io
\(dp[i][j][s]=\sum \{dp[i-1][j-cnt(s)][s']|((s'<<1)\&s)||((s'>>1)\&s)||(s' \&s)==0\}\)ast
其中,s'表示上一行的状态,要保证互不侵犯,则:class
国王周围的八个格子显然都不能够再放国王,那么显然从0~(1<<n)的状态并非均可以取到的(就如\(3_{10}\to 011_{2}\)显然就是不符合要求的),那么咱们就不能简单的从0~(1<<n)枚举子集(为何要说这个呢,由于我就直接枚举了,而后就炸了√)
须要进行dfs来把合法的子集处理出来,这里用了两个数组
num[S] 记录状态为S的摆法中,有多少个1
kind[Cnt] 记录一种符合要求的摆法,其中Cnt是符合要求的摆法的总个数
具体代码以下:
void dfs(int cnt,int now,int nu,int bj){ if(cnt==n+1) return; if(num[now]==-1) kind[++Cnt]=now; num[now]=nu; if(bj==0) dfs(cnt+1,now+(1<<cnt),nu+1,1); dfs(cnt+1,now,nu,0); }
dfs也就是处理长度为n的一行每两个1相隔至少为1个空位置(0)的摆法
其中:
cnt 表示当前枚举到了第几位
now 表示当前状态下,二进制所对应的十进制是多少
nu 表示目前一共填了几个1
bj 用于判断上一位填的是1仍是0
其中0表示上一位填的0 1表示上一位填的1
显然若是上一位填的是1,那么这一位咱们只能填0,所以当bj==1时,咱们只dfs这一位填0的状况,不然dfs两种状况。由于会重复搜索到(鬼知道为何,因此要判断以前num[now]是否!=-1了(以前是判断!=0来着,后来发现num[0]就等于0),若是=-1,就须要记录下来,!=-1,就不须要再记录一遍了.
初始状态是\(dp[1][num[kind[i]]][kind[i]]=1\)
也就是第一行的国王,每一种状态都有一种方法√;
而后就能够愉快的AC
#include<bits/stdc++.h> using namespace std; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch>'9'||ch<'0') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } int n,k,Cnt; long long dp[10][100][1000]; int num[1000],kind[1000]; void dfs(int cnt,int now,int nu,int bj){ if(cnt==n+1) return; if(num[now]==-1) kind[++Cnt]=now; num[now]=nu; if(bj==0) dfs(cnt+1,now+(1<<cnt),nu+1,1); dfs(cnt+1,now,nu,0); } bool cmp(int a,int b) { return a<b; } int main(){ scanf("%d%d",&n,&k); memset(num,-1,sizeof(num)); dfs(0,0,0,0); sort(kind+1,kind+Cnt+1,cmp); for(int i=1;i<=Cnt;i++) dp[1][num[kind[i]]][kind[i]]=1; for(int i=2;i<=n;i++) { for(int a=1;a<=Cnt;a++) { for(int b=1;b<=Cnt;b++) { if(kind[a]&kind[b]) continue; if(kind[a]&(kind[b]<<1)) continue; if(kind[a]&(kind[b]>>1)) continue; for(int j=k;j>=num[kind[a]];j--) dp[i][j][kind[a]]+=dp[i-1][j-num[kind[a]]][kind[b]]; } } } long long ans=0; for(int i=1;i<=Cnt;i++) ans+=dp[n][k][kind[i]]; printf("%lld",ans); return 0; }