[蓝桥杯 2021 省 AB2] 国际象棋
题目
Description
众所周知, “八皇后” 问题是求解在国际象棋棋盘上摆放 8 个皇后,使得两两之间互不攻击的方案数。已经学习了很多算法的小蓝觉得 “八皇后” 问题太简单了,意犹末尽。作为一个国际象棋迷,他想研究在 N×M 的棋盘上,摆放 K 个马,使得两两之间互不攻击有多少种摆放方案。由于方案数可能很大,只需计算答案除以 1000000007 (即 1e9+7) 的余数。
如下图所示,国际象棋中的马摆放在棋盘的方格内,走 “日” 字, 位于 (x,y) 格的马(第 x 行第 y 列)可以攻击 (x+1,y+2),(x+1,y−2),(x−1,y+2),(x−1,y−2),(x+2,y+1),(x+2,y−1),(x−2,y+1),(x−2,y−1) 共 8 个 格子。
Input
输入一行包含三个正整数 N,M,K 分别表示棋盘的行数、列数和马的个数。
Output
输出一个整数,表示摆放的方案数除以 1000000007( 即 109+7)的余数。
Sample Input
4 4 3
Sample Output
276
思路
一道状压$dp$题;
与[SCOI2005] 互不侵犯差不多,只不过因为马可以攻击到下两行的地方,所以需要多开一维数组存上一行的状态;
用$dp[i][j][x][y]$表示第$i$行的状态$x$,第$i-1$行状态$y$,并且到第$i$行为止一共放了$j$个马;
状态转移方程就为: $dp[i][j+sum][x][y]+=dp[i-1][j][y][z]$
$sum$为当前行的马的数量,$z$表示上上行的状态;
代码
#include<bits/stdc++.h> typedef long long ll; using namespace std; const ll mod=1e9+7; ll n,m,k; ll a[101][7],dp[101][21][1<<6][1<<6]; int main() { scanf("%lld%lld%lld",&m,&n,&k); dp[0][0][0][0]=1; for(ll i=1;i<=n;i++) for(ll x=0;x<=(1<<m)-1;x++) { ll sum=0; for(ll j=1;j<=m;j++) if(x&(1<<(j-1))) sum++;//统计当前行马的数量 for(ll y=0;y<=(1<<m)-1;y++) for(ll z=0;z<=(1<<m)-1;z++) { if(x&(z>>1)||x&(z<<1))//上上行与当前行互不攻击 continue; if(x&(y>>2)||x&(y<<2))//上一行与当前行互不攻击 continue; for(ll j=0;j<=k-sum;j++)//枚举到上一行为止马的总数 dp[i][j+sum][x][y]=(dp[i][j+sum][x][y]+dp[i-1][j][y][z])%mod; } } ll ans=0; for(ll x=0;x<=(1<<m)-1;x++) for(ll y=0;y<=(1<<m)-1;y++) ans=(ans+dp[n][k][x][y])%mod; printf("%lld",ans); return 0; }