在一个N行M列的棋盘上,让你放若干个炮,可以是0个,使得没有一个炮可以攻击另一个炮,请问有多少种放置方法。
题解:
因为每一行每一列的炮的数量<=2
考虑开dp数组存储有几列放了一个炮,有几列放了两个炮
dpi[k],表示放了前i行,有j列是有一个棋子,有k列是有2个棋子的合法方案数
接下来分类讨论:
一、不放棋子
二、放一个棋子
放在一个棋子的列:
我们在某一个有一个棋子的列放置棋子,会使这一列变成有两个棋子。
即我们要得到dpi[k]需要在j+1个有一个棋子的列放置棋子,变为有j个有一个棋子的列
而我们有会得到新的有两个棋子的列,因此我们之前必须有k-1个有两个棋子的列。
放在没有棋子的列:
在一个没有棋子的列放置棋子,会得到一个新的有一个棋子的列,即我们要从j-1得到j。
而这时候,我们有两个棋子的列的数量不会变。可以在空列中的任何一列放置这个棋子。
三、放两个棋子
一个放在有一个棋子的列,一个放在没有棋子的
都放在没有棋子的列
都放在有一个棋子的列
#include<bits/stdc++.h> using namespace std; const int maxn=114; const int mod=9999973; typedef long long ll; int N,M; ll ans; ll dp[maxn][maxn][maxn]; ll cal (int x) { return (x*(x-1)/2)%mod; } int main () { scanf("%d%d",&N,&M); dp[0][0][0]=1; for (int i=1;i<=N;i++) { for (int j=0;j<=M;j++) { for (int k=0;k<=M-j;k++) { dp[i][j][k]=dp[i-1][j][k]; if (k>=1) dp[i][j][k]+=dp[i-1][j+1][k-1]*(j+1); if (j>=1) dp[i][j][k]+=dp[i-1][j-1][k]*(M-j-k+1); if (k>=2) dp[i][j][k]+=dp[i-1][j+2][k-2]*(((j+2)*(j+1)/2)); if (k>=1) dp[i][j][k]+=dp[i-1][j][k-1]*j*(M-j-k+1); if (j>=2) dp[i][j][k]+=dp[i-1][j-2][k]*cal(M-j-k+2); dp[i][j][k]%=mod; } } } for (int i=0;i<=M;i++) for (int j=0;j<=M;j++) ans+=dp[N][i][j],ans%=mod; printf("%lld\n",(ans+mod)%mod); return 0; }