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