知识点: 状压DP
原题面
发现 \(n,m \le9\) ,又要求方案数 ,
就知道这必然是个状压了.
分析题意:
一些性质:
对于每一行 的棋子摆放情况 , 只会被相邻的两行棋子所影响
对于两行棋子能否相邻 , 处理方式较麻烦
但是 \(n,m\le 9\) , 状态数较少 ,
可以预处理出所有单行 状态 , 及他们能相邻 的 其他单行状态
算法实现:
先预处理出 单行 的 合法状态 . (即没有 左右相邻 的 国王的状态)
并记录此单行状态 的 国王个数 .
再预处理出 能直接相邻 的单行状态 . (检查每个国王相邻 \(8\) 格内是否有其他国王)
之后开始状压 \(DP\)
设 \(f[i][j][k]\) 表示 , 第 \(i\) 行状态为 \(j\) , \(1~i\) 行已经放置了 \(k\) 个国王的方案数
三层循环枚举 当前行数 \(i\) , 第 \(i-1\) 行的状态 , 第 \(i\) 行的状态
然后 检查枚举的两行状态是否 能够相邻.
如果可以相邻 , 继续枚举 第 \(1~i\) 行的国王数量,
再用 \(i-1\) 行的方案数 更新 第i行的方案数
转移方程式:
\(f[i][j][k] = \sum f[i-1][l][k-num[l]]\)
(\(l\) 与 \(j\) 是可以相邻的合法状态 , \(num\) 记录每个单行状态的国王数量)
一些小技巧:
记录合法状态时 ,可以使用 \(vector\) 类.
直接将合法状态压入 \(vector\) 中,
枚举状态时 , 用状态在 \(vector\) 中的编号 映射 状态,
以避免不必要的枚举 , 浪费时间空间 , 增长代码量.
#include
#include
#include
#define max(a,b) ((a)>(b)?(a):(b))
#define lowbit(x) ((x)&(-x))
//=============================================================
int n,k , all,size ,quan[520];
std::vector plan;//存单行合法状态
bool map[520][520];//记录两状态能否相邻
long long ans,f[15][100][520];
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
bool check(int x,int y)//检查x,y两状态能否相邻
{
while(x && y)
{
//如果x相邻三位中 , 能与y的一位同为1 , 那么不合法
if(((x>>1)&y) || ((x<<1)&y) || (x&y)) return 0;
x>>=1,y>>=1;
}
return 1;
}
void prepare()
{
n=read(),k=read(); all=(1<>1)) {flag=1;break;}
if(flag || sum>k) continue;//有相邻国王,或者单行数量>总数量 , 不合法
plan.push_back(i);//加入vector中
f[1][sum][plan.size()-1]=1;//初始化i=1时的答案
quan[plan.size()-1]=sum;//记录数量
}
size=plan.size();//检查各状态能否相邻
for(int i=0; i