【状态压缩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 } 

 

posted @ 2016-07-05 21:16  iiyiyi  阅读(231)  评论(0编辑  收藏  举报