状态压缩DP
题意:
Problem Description
郑厂长不是正厂长
也不是副厂长
他根本就不是厂长
还是那个腾讯公司的码农
一个业余时间喜欢下棋的码农
最近,郑厂长对八皇后问题很感兴趣,拿着国际象棋研究了好几天,终于研究透了。兴奋之余,坐在棋盘前的他又开始无聊了。无意间,他看见眼前的棋盘上只摆了八个皇后,感觉空荡荡的,恰好又发现身边还有几个骑士,于是,他想把这些骑士也摆到棋盘上去,当然棋盘上的一个位置只能放一个棋子。因为受八皇后问题的影响,他希望自己把这些骑士摆上去之后,也要满足每2个骑士之间不能相互攻击。
现在郑厂长想知道共有多少种摆法,你能帮助他吗?
骑士的下法:
每步棋先横走或直走一格,然后再往外斜走一格;或者先斜走一格,最后再往外横走或竖走一格(即走“日”字)。可以越子,没有”中国象棋”的”蹩马腿”限制。Input
输入第一行为一个整数T(1<=T<=8),表示有T组测试数据;
每组数据首先是一个整数N(1<=n<=10),表示要摆N个骑士上去;
接下来是一个8∗8的矩阵来描述一个棋盘,’.’表示这个位置是空的,′∗′ 表示这个位置上已经放了皇后了;
数据中的初始棋盘保证是一个合法的八皇后摆法。Output
对每组数据,请在一行内输出一个整数,表示合法的方案数。
Sample Input
2 1 *....... ....*... .......* .....*.. ..*..... ......*. .*...... ...*.... 2 *....... ....*... .......* .....*.. ..*..... ......*. .*...... ...*....
Sample Output
56 1409
思路:
就像前一个郑厂长系列一样,棋盘的状态需要压缩。不同的是这里不是求出最多放几个马,而是给出马的数量问有多少种在限制条件下放的可能数。显然之前的三维已经无法满足这个状态,需要增加一维马的数量去遍历每一行的所有状态。
定义:dp[r][number][i][j] 为0~r行一共放置了number个马满足的所有情况,第r行状态为i,r-1行状态为j。
当算到第r行状态为i的时候要加上r-1行所有的满足的状态j的个数,便是所有此状态情况,然后遍历第r行状态,最后求和既可。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 260;
int num[maxn],pos;
int map[10];
int dp[8][12][maxn][maxn];
void init()
{
memset(dp,0,sizeof(dp));
memset(map,0,sizeof(map));
}
void state_num()
{
pos = 0;
for(int i = 0;i < (1<<8); i++) {
int k = i;
int number = 0;
while(k) {
if(k&1) number++;
k >>= 1;
}
num[pos++] = number;
}
}
int main()
{
// freopen("in.txt","r",stdin);
int tt;
scanf("%d",&tt);
state_num();
while(tt--) {
init();
int k;
scanf("%d",&k);
getchar();
for(int i = 0;i < 8; i++) {
for(int j = 0;j < 8; j++){
char c;
scanf("%c",&c);
if(c == '*') {
map[i] += (1<<j);
}
}
getchar();
}
for(int i = 0;i < pos; i++) {
if(i&map[0]) continue;
if(num[i] > k) continue;
dp[0][num[i]][i][0] = 1;
}
for(int number = 0;number <= k; number++) {
for(int i = 0;i < pos; i++) {
if(i&map[1]) continue;
if(num[i] > number) continue;
for(int j = 0;j < pos; j++) {
if(j&map[0]) continue;
if((i<<2)&j || (i>>2)&j) continue;
dp[1][number][i][j] += dp[0][number-num[i]][j][0];
}
}
}
for(int r = 2;r < 8; r++) {
for(int number = 0;number <= k; number++) {
for(int i = 0;i < pos; i++) {
if(i&map[r]) continue;
if(num[i] > number) continue;
for(int j = 0;j < pos; j++) {
if(j&map[r-1]) continue;
if((i<<2)&j || (i>>2)&j) continue;
for(int t = 0;t < pos; t++) {
if(t&map[r-2]) continue;
if((i<<1)&t || (i>>1)&t) continue;
if((j<<2)&t || (j>>2)&t) continue;
dp[r][number][i][j] += dp[r-1][number-num[i]][j][t];
}
}
}
}
}
int ans = 0;
for(int i = 0;i < pos; i++) {
for(int j = 0;j < pos; j++) {
ans += dp[7][k][i][j];
}
}
printf("%d\n",ans);
}
return 0;
}