【状态压缩DP】BZOJ1087-[SCOI2005]互不侵犯King
【题目大意】
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。
【思路】
先预处理每一行可行的状态(即单行中左右没有相邻的1),存放到usable中。
然后预处理usable中两两之间能否相互转换,存放到map中。
f[i][j][k]第i行,已用去j个国王,当前行状态为usable[k]的情况,四重循环暴力递推即可!
这里用了usable按道理应该会快一些,但是和popoqqq的速度是一样的?不过这个优化应该是有效的!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long ll; 7 const int MAXN=512; 8 const int MAXK=100; 9 const int MAXM=15; 10 int n,m; 11 int map[MAXN][MAXN]; 12 ll f[MAXM][MAXK][MAXN];//f[i][j][k]第i行,已用去j个国王,当前行状态为k 13 int usable[MAXN]; 14 int digit[MAXN]; 15 16 int Usable(int x) 17 { 18 if (x<<1&x || x>>1&x) return 0;else return 1; 19 //不能有相邻的1 20 } 21 22 int get_digit(int x) 23 { 24 int ret=0; 25 while (x) ret+=x&1,x>>=1; 26 return ret; 27 } 28 29 int Judge(int x,int y) 30 { 31 if (x&y || (x<<1)&y || (x>>1)&y) return 0;else return 1; 32 /*不可行的情况有三种*/ 33 } 34 35 void init() 36 { 37 memset(usable,0,sizeof(usable)); 38 memset(map,0,sizeof(map)); 39 for (int i=0;i<1<<n;i++) 40 if (Usable(i)) usable[++usable[0]]=i; 41 /*预处理可行的状态(左右不能有相邻的1)*/ 42 for (int i=1;i<=usable[0];i++) 43 for (int j=1;j<=usable[0];j++) 44 { 45 if (Judge(usable[i],usable[j])) map[i][j]=1; 46 } 47 /*预处理可行的状态中能够转换的状态*/ 48 for (int i=1;i<=usable[0];i++) digit[i]=get_digit(usable[i]); 49 /*预处理每一个可行状态中1的个数*/ 50 } 51 52 ll dp() 53 { 54 memset(f,0,sizeof(f)); 55 f[0][0][1]=1; 56 for (int i=1;i<=n;i++) 57 for (int j=0;j<=m;j++) 58 for (int k=1;k<=usable[0];k++) 59 if (digit[k]<=j) 60 { 61 for (int l=1;l<=usable[0];l++) 62 { 63 if (map[l][k] && digit[l]+digit[k]<=j) 64 f[i][j][k]+=f[i-1][j-digit[k]][l]; 65 } 66 } 67 ll ret=0; 68 for (int i=1;i<=usable[0];i++) ret+=f[n][m][i]; 69 return ret; 70 } 71 72 int main() 73 { 74 scanf("%d%d",&n,&m); 75 init(); 76 cout<<dp()<<endl; 77 return 0; 78 }