题目描述
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入输出格式
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
输入输出样例
输入样例#1:
1 3
输出样例#1:
7
说明
样例说明
除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有222-1=7种方案。
数据范围
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6
一开始看以为是状压
但数据范围显然不支持状压==
膜题解后发现棋子的放置顺序对答案煤油影响
所以只需要考虑放0,1,2枚棋子的有几列而不需要考虑其具体位置
f[i][j][k] 表示前i行有j列有1个棋子,k列有2个棋子
所以有m - j - k列放了0个棋子
假设这一行煤油放棋子
暴力转移
假设这一行放1枚棋子
可以使有0个棋子的列变为1个
可以使有1个棋子的列变为2个
如果这一行放2枚棋子
可以使两个有1个棋子的列变成2个.
也可以使两个有0个棋子的列变成1个 .
也可以使一个有0个棋子的列和一个有1个棋子的列变成一个有1个棋子的列和一个有2个棋子的列
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
# define LL long long
const int M = 105 ;
const int mod = 9999973 ;
using namespace std ;
int n , m ;
LL f[M][M][M] , Ans ;
inline int C(int n , int m) {
return n * (n - 1) / 2 ;
}
int main() {
cin >> n >> m ;
f[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 ++) {
f[i][j][k] = (f[i][j][k] + f[i - 1][j][k])%mod ;
if(j > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j - k + 1))%mod ;
if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1))%mod ;
if(j > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j - k + 2 , 2))%mod ;
if(k > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2 , 2))%mod ;
if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - k + 1))%mod ;
}
}
for(int i = 0 ; i <= m ; i ++)
for(int j = 0 ; j <= m ; j ++)
Ans = (Ans + f[n][i][j])%mod ;
cout << Ans << endl ;
return 0 ;
}