版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/89401275
title
BZOJ 1801
LUOGU 2051
Description
在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.
Input
一行包含两个整数N,M,中间用空格分开.
Output
输出所有的方案数,由于值比较大,输出其mod 9999973
Sample Input
1 3
Sample Output
7
HINT
除了在3个格子中都放满炮的的情况外,其它的都可以.
100%的数据中N,M不超过100
50%的数据中,N,M至少有一个数不超过8
30%的数据中,N,M均不超过6
Source
analysis
首先声明:本题正解和 没有一丝关系。
- 在
学长查找了
上的众多题解后,得出结论:这道题考虑前50,裸的
进制状压DP
表示到第i行且前面列的状态为 ( 放了 个, 放了 个, 没有放)但是 进制状压DP好像很难,想学的话,建议写一下POJ 1038。 - 正解:我们可以设状态为
表示到第
行时,有
列有一个炮,
列有两个炮。
这样就有三种情况:- 第 行不放炮: 。
- 第
行只放一个炮
- 放在了原来没有炮的一列
- 放在了原来有一个炮的一列
- 第
行放两个炮
- 都放在了原来没有炮的一列
- 一个放在了原来有一个炮的一列,一个放在了原来没有炮的一列
- 都放在了原来有一个炮的一列
- 都放在了原来没有炮的一列
- 初态:
- 末态:
- 那么我们为什么要这么设状态呢?
- 首先以行为阶段,根据象棋的规则,在同一行中,至多只能有两个炮,同理:在同一列中,至多只能有两个炮
- 思考一个可以覆盖整个状态空间的 数组: 表示到了第 行
- 接下来我们想:某列中的炮能否通过位运算求得?
- 我们能够发现,可能我们目前在第 行,但是在某个 行的 列有一个炮,我们要知道第 行的第 列能否放置炮。
- 但是 可能与 相差甚远,我们不能直接通过位运算得到,逐行枚举又会耗费大量不必要的时间。
- 那么我们就干脆将列的状态记录在数组里。
- 我们想我们其实并不关心到第 行时哪一列有 个炮,哪一列有两个炮,我们只需要知道到第 行时,有多少列有 个炮,有多少列有 个炮,剩下的问题我们能够通过枚举状态解决。
参考资料:Cyxhsa
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=101,mod=9999973;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
ll f[maxn][maxn][maxn];
inline int cnt(int x)
{
return x*(x-1)/2;
}
int main()
{
int n,m;
read(n);read(m);
f[0][0][0]=1;
for (int i=0; i<n; ++i)
for (int j=0; j<=m; ++j)
for (int k=0; k+j<=m; ++k)
if (f[i][j][k])
{
(f[i+1][j][k]+=f[i][j][k])%=mod;//第i行不放炮
if (m-j-k>=1)//第i行只放一个炮,并且放在了原来没有炮的一列
(f[i+1][j+1][k]+=f[i][j][k]*(m-j-k))%=mod;
if (j>=1)//第i行只放一个炮,并且放在了原来有一个炮的一列
(f[i+1][j-1][k+1]+=f[i][j][k]*j)%=mod;
if (m-j-k>=2)//第i行放两个炮,并且都放在了原来没有炮的一列
(f[i+1][j+2][k]+=f[i][j][k]*cnt(m-j-k))%=mod;
if (m-j-k>=1 && j>=1)//第i行放两个炮,并且一个放在了原来有一个炮的一列,一个放在了原来没有炮的一列
(f[i+1][j][k+1]+=f[i][j][k]*j*(m-j-k))%=mod;
if (j>=2)//第i行放两个炮,并且都放在了原来有一个炮的一列
(f[i+1][j-2][k+2]+=f[i][j][k]*cnt(j))%=mod;
f[i][j][k]%=mod;
}
ll ans=0;
for (int i=0; i<=m; ++i)
for (int j=0; j+i<=m; ++j)
ans=(ans+f[n][i][j])%mod;
printf("%lld\n",ans);
return 0;
}