【状态压缩dp】bzoj1087: [SCOI2005]互不侵犯King

状态压缩dp经典

Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案数。

Sample Input

3 2

Sample Output

16

题目分析

第一眼看上去是爆搜题?(不过应该会TLE)

这里每一行的放置显然只和上一行有关系,自然考虑将状态压缩扔进dp状态里。

用$f[i][j][k]$表示前$i$行放置了$j$个国王,第$i$行状态是$k$的方案数。

有一个技巧,在处理出合法的状态之后,$O(statuses^2)$地处理这个状态下一行能够放的状态。

转移方程不难想到,不过要注意的是仍然是拓扑序的问题。dfs下去显然是不行的,于是可以先枚举层数,再枚举当前层状态,最后枚举当前层安排的国王总数。这样被动转移下去就好了。

 1 #include<bits/stdc++.h>
 2 const int maxn = 1305;
 3 const int maxm = 1005;
 4 
 5 int n,k,mx;
 6 long long ans;
 7 long long f[13][203][maxn];
 8 int edgeTot,head[maxn],nxt[maxm],edges[maxm];
 9 int num[maxn],per[maxn];
10 bool vis[maxn];
11 
12 int read()
13 {
14     char ch = getchar();
15     int num = 0;
16     bool fl = 0;
17     for (; !isdigit(ch); ch = getchar())
18         if (ch=='-') fl = 1;
19     for (; isdigit(ch); ch = getchar())
20         num = (num<<1)+(num<<3)+ch-48;
21     if (fl) num = -num;
22     return num;
23 }
24 void addedge(int u, int v)
25 {
26     edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
27 }
28 int legal(int x)
29 {
30     int cnt = 0;
31     for (int pre=0; x; x>>=1)
32     {
33         int t = x&1;
34         if (pre&t) return 0;
35         cnt += (pre = t)?1:0;
36     }
37     return cnt;
38 }
39 void init()
40 {
41     per[++per[0]] = 0;
42     for (int i=1; i<mx; i++)
43     {
44         num[i] = legal(i);
45         if (num[i])
46             vis[i] = 1, per[++per[0]] = i;
47     }
48     addedge(0, 0);
49     for (int i=1; i<=per[0]; i++)
50     {
51         for (int j=i+1; j<=per[0]; j++)
52         {
53             int x = per[i], y = per[j];
54             if ((x&y)||(x&(y<<1))||(x&(y>>1))) continue;
55             addedge(x, y), addedge(y, x);
56         }
57     }
58 }
59 int main()
60 {
61     memset(head, -1, sizeof head);
62     n = read(), k = read(), mx = 1<<n;
63     init();
64     f[0][0][0] = 1;
65 //    dp(0, 0, 0);        记忆化搜索的形式应该是不行的
66     for (int i=0; i<n; i++)
67         for (int t=(i?per[0]:1); t; t--)
68         {
69             int u = per[t];
70             for (int p=head[u]; p!=-1; p=nxt[p])
71             {
72                 int v = edges[p];
73                 for (int q=num[u]; q<=k-num[v]; q++)
74                     f[i+1][q+num[v]][v] += f[i][q][u];
75             }
76         }
77     for (int i=1; i<=per[0]; i++)
78         ans += f[n][k][per[i]];
79     printf("%lld\n",ans);
80     return 0;
81 }

 

 

END

posted @ 2018-07-22 19:36  AntiQuality  阅读(166)  评论(0编辑  收藏  举报