BZOJ1087 [SCOI2005] 互不侵犯King

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1087

Description

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

Input

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

Output

方案数。

 

状态压缩DP初探。f[i][j][k]表示第i行,共放了j个国王,k表示当前该行的状态(状态压缩)。

预处理过程为pre(),包括:

1. 判断一行中的一个状态i是否合法(没有两个1相邻,国王之间不会在左右方向互相攻击),合法则c[i]=1;

2. 计算一个合法状态i放了几个国王,记录在cnt[i]中;

3. 判断两个状态i, j之间i转移到j是否合法(国王之间不会在上-下,左上-右下,左下-右上方向互相攻击),合法则valid[i][j]=1。

小改了改代码时间从44ms变为28ms,看来单单减少循环就很重要了(然并卵,整整49个0ms……)

 1 #include <cstdio>
 2 #define rep(i,l,r) for(int i=l; i<=r; i++)
 3 #define clr(x,y) memset(x,y,sizeof(x))
 4 typedef long long ll;
 5 using namespace std;
 6 int n,m,all,cnt[512];
 7 ll ans,f[10][82][512];
 8 bool c[512],valid[512][512];
 9 void pre(){
10     rep(i,0,all) if (!(i & (i >> 1))){
11         int s = 0;
12         for (int j=i; j; j>>=1) s += j & 1;
13         cnt[i] = s; c[i] = 1;
14     }
15     rep(i,0,all) if (c[i])
16     rep(j,0,all) if (c[j]){
17         if ((!(i & j)) && (!(i & (j >> 1))) && (!(j & (i >> 1))))
18         valid[i][j] = 1;
19     }
20 }
21 int main(){
22     scanf("%d%d",&n,&m); all = (1 << n) - 1;
23     pre();
24     rep(i,0,all) if (c[i]) f[1][cnt[i]][i] = 1;
25     rep(i,1,n-1){
26         rep(j,0,all) if (c[j])
27         rep(k,0,all) if (c[k]){
28             if (valid[j][k])
29             for (int l=cnt[j]; l+cnt[k]<=m; l++)
30             f[i+1][l+cnt[k]][k] += f[i][l][j];
31         }
32     }
33     rep(i,0,all) ans += f[n][m][i];
34     printf("%lld\n",ans);
35     return 0;
36 }
View Code

 

posted on 2015-12-08 17:41  ACMICPC  阅读(248)  评论(0编辑  收藏  举报

导航