题目描述
在一个 n × m n \times m n×m 的棋盘上,放置很多的炮(也可以一个也不放),使得没有一个炮可以攻击到另一个炮,有多少种放置方法?
答案可能很大,请你将答案对 9999973 9999973 9999973 取模。
炮的攻击方式是越过同一行或同一列的一个棋子打到后面的下一个棋子。
输入格式
一行两个整数 n , m n, m n,m,表示行和列。
输出格式
一个整数,表示方案数对 9999973 9999973 9999973 取模的结果。
样例
样例输入1:
1 3
样例输出1:
7
样例1解释:
除了一行中
3
3
3 格都放满炮的情况,其他都可以。
数据范围
对于
30
%
30\%
30% 的数据,
1
≤
n
,
m
≤
6
1 \le n, m \le 6
1≤n,m≤6。
对于
60
%
60\%
60% 的数据,
n
,
m
n, m
n,m 至少有一个不超过
8
8
8。
对于
100
%
100\%
100% 的数据,
1
≤
n
,
m
≤
100
1 \le n, m \le 100
1≤n,m≤100。
题解
两个炮不互相攻击,相当于每行,每列上最多放 2 2 2 个炮。
由于直接搜索会超时,所以考虑进行 dp
或记忆化搜索。
d p i , j , k dp_{i, j, k} dpi,j,k 表示第 i i i 行,有 j j j 列上有 1 1 1 个炮, k k k 列上有 2 2 2 个炮。
接下来考虑转移,有以下几种情况:
- 第 i i i 行什么也不放,此时 d p i , j , k + = d p i − 1 , j , k dp_{i, j, k} += dp_{i - 1, j, k} dpi,j,k+=dpi−1,j,k。
- 第 i i i 行放一个炮。
- 第 i i i 行放两个炮。
初始化 d p 0 , 0 , 0 = 1 dp_{0, 0, 0} = 1 dp0,0,0=1,记得取模。
代码:
long long f[110][110][110];
f[0][0][0] = 1;
//转移
for(int i = 1; i <= n; ++ i){//第 i 行
for(int j = 0; j <= m; ++ j){//j 列有 1 个炮
for(int k = 0; k <= m - j; ++ k){//k 列有 2 个炮
f[i][j][k] = f[i - 1][j][k];//1
if(j){//2.1
f[i][j][k] += f[i - 1][j - 1][k] * (m - j - k + 1);
}
if(j < m && k){//2.2
f[i][j][k] += f[i - 1][j + 1][k - 1] * (j + 1);
}
if(j >= 2){//3.1
f[i][j][k] += f[i - 1][j - 2][k] * (m - j - k + 2)
* (m - j - k + 1) / 2;
}
if(j >= 1 && k){//3.2
f[i][j][k] += f[i - 1][j][k - 1] * j * (m - j - k + 1);
}
if(j <= m - 2 && k >= 2){//3.3
f[i][j][k] += f[i - 1][j + 2][k - 2] * (j + 1)
* (j + 2) / 2;
}
f[i][j][k] %= 9999973;
}
}
}
//统计答案
long long ans = 0;
for(int i = 0; i <= m; ++ i){
for(int j = 0; j <= m - i; ++ j){
ans += f[n][i][j];
ans %= 9999973;
}
}