【bzoj1801】[Ahoi2009]chess 中国象棋 dp
题目描述
在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.
输入
一行包含两个整数N,M,中间用空格分开.
输出
输出所有的方案数,由于值比较大,输出其mod 9999973
样例输入
1 3
样例输出
7
题解
dp
首先必须知道,每行最多有两个炮,每列也最多有两个炮。
那么我们只需要记录某棋盘有一个炮的列数和有两个炮的列数,然后讨论一行放炮的个数及位置即可。
设f[i][j][k]表示前i行,有j列有一个炮,有k列有两个炮的方案数。
那么对于每行有以下6种情况:
1.不放炮
2.放一个炮在没有炮的列上
3.放一个炮在有一个炮的列上
4.放两个炮在没有炮的列上
5.放一个炮在没有炮的列上,放一个炮在有一个炮的列上
6.放两个炮在有一个炮的列上
用乘法原理和排列组合求出每种情况可选择的方案数,乘上原方案数,累加到新方案数上。
注意一下边界什么的就好了。
还有这道题需要开long long,否则极限情况乘法会爆int。
#include <cstdio> #define MOD 9999973 typedef long long ll; ll f[110][110][110]; void calc(ll &a , const ll b) { a = (a + b) % MOD; } int main() { int n , m , i , j , k; ll ans = 0; scanf("%d%d" , &n , &m); f[0][0][0] = 1; for(i = 1 ; i <= n ; i ++ ) { for(j = 0 ; j <= m ; j ++ ) { for(k = 0 ; k <= m ; k ++ ) { calc(f[i][j][k] , f[i - 1][j][k]); if(j) calc(f[i][j][k] , f[i - 1][j - 1][k] * (m - j + 1 - k)); if(k) calc(f[i][j][k] , f[i - 1][j + 1][k - 1] * (j + 1)); if(j >= 2) calc(f[i][j][k] , f[i - 1][j - 2][k] * (m - j + 2 - k) * (m - j + 1 - k) / 2); if(j && k) calc(f[i][j][k] , f[i - 1][j][k - 1] * j * (m - j - k + 1)); if(k >= 2) calc(f[i][j][k] , f[i - 1][j + 2][k - 2] * (j + 2) * (j + 1) / 2); } } } for(i = 0 ; i <= m ; i ++ ) for(j = 0 ; j <= m ; j ++ ) calc(ans , f[n][i][j]); printf("%lld\n" , ans); return 0; }