79
题目大意:给你一个N*N的棋盘,要你在其中放入K个国王,每个国王会攻击到以它为中心的九宫格的相邻8个位置,求方案总数.
这道题可以用状态压缩的思想来做.
我们在进行DP的时候,我们可以通过已知前 i-1 行的最优解来得出前i行的最优解,那么这就涉及到一个问题,我们如何来表示每一行的状态呢?
这也就是状态压缩的动机.如果我们不能够很方便的来表示状态,我们可以把很多维的状态,每一维的范围相同,那么可以把这些维合并成一个X进制数.
用X进制数来表示这个状态.
要了解详情的,可以去参考 郑州101中学/天津大学 周伟的论文《状态压缩》
这道题目我们也可以用SCR来做。
具体思路:
1、求出每一行可能的所有状态数(也就是用二进制来表示这一行某一列是否放入国王)
2、进行动态规划:
用F [ i ] [ j ] [ k ]来表示前i行放入k个国王,并且第 i 行的状态为第 j 个状态。
那么F[ i ] [ j ] [ k ] += F [ i-1][ p ][ k - c[i]],也就是对于前i-1行,我们枚举第i-1行的所有可能状态,
并且保证状态 j和状态p没有同一位放置国王(可以用位运算来完成),
由于这个国王可以攻击8个周围的点,所以我们有必要将i-1行的状态进行左移和右移的操作,来进行判断
并且k-c[i]>=c[p].
这样就可以进行转移了,枚举每一种状态,得到的所有求个和便得到答案了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 200,MAXS = 300,MAXPN = 200;
int s[MAXS],c[MAXS];
LL f[2][MAXS][MAXPN],ans;
int n,pn;
void dfs(int p,int last,int now,int cnt){
if (p == n){s[++s[0]]=now;c[s[0]]=cnt;return ;}
dfs(p+1,0,now*2,cnt);
if (!last) dfs(p+1,1,now*2+1,cnt+1);
}
int main(){
cin >> n >> pn;
if (pn>pow((n+1)/2,2)){
cout << 0 << endl;
return 0;
}
dfs(0,0,0,0);
f[0][1][0]=1;
for (int i=1;i<=n;++i){
for (int j=1;j<=s[0];++j)
for (int k=c[j];k<=pn;++k)
for (int p=1;p<=s[0];++p)
if (!(s[p] & s[j]) && ! (s[p] & (s[j] << 1)) && !(s[p] &(s[j] >> 1)) && (k-c[j]>=c[p]))
f[i & 1][j][k] += f[1-i&1][p][k-c[j]];
memset(f[1-i&1],0,sizeof(f[1-i&1]));
}
for (int i=1;i<=s[0];++i) ans += f[n&1][i][pn];
cout << ans << endl;
return 0;
}