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 }