题目描述
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入输出格式
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
输入输出样例
输入样例#1:
1 3
输出样例#1:
7
说明
样例说明
除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。
数据范围
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6
/* 清北学堂的老师讲过这道题目 题目可以转化为,在棋盘放旗子要求每行每列都至多有两个棋子的方案数 dp[i][j][k]表示前i行 有j列放了1个 棋子,有k列放了2个棋子 那么有(列数m - j - k)列放了0个棋子 然后考虑转移 dp[i][j][k]的转移要考虑当前列放几枚棋子 1: 当前列不放棋子 这时候的方案数和上一列一样 2:当前列放1个棋子 那么就要讨论放到了的那一列原本有几枚棋子 假设那一列没有,则dp[i][j][k]从dp[i - 1][j - 1][k] 转移而来,同时又(m - (j - 1) - k)种方法,乘法原理 假设那一列有一个,则 dp[i][j][k]从dp[i - 1][j + 1][k - 1] 转移而来同时有j + 1种方法乘法原理 3.当前列放两个棋子 那么要讨论放法 假设放到了两个空行 那么从dp[i - 1][j - 2][ k]转移而来,同时又C(m - ( j - 2) - k,2 )种方法 乘法原理 假设放到了两个有一个棋子的行 那么从 dp[i - 1][j + 2][ k - 2]转移而来,同时有C(j + 2,2)种方法 乘法原理 假设一个放到了空行,一个放到了有一个棋子的行 那么从dp[i - 1][j][k - 1]转移而来 同时有j * (k - 1)种方法乘法原理 最后统计所有可能的解,相加输出即可 */ #include<cstdio> #include<algorithm> #include<iostream> #define MOD 9999973 using namespace std; int n,m; long long dp[131][131][131]; int c(int x) { return (x * x - x) / 2; } 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;j + k <= m;k++) { /*按照上面说的来*/ dp[i][j][k] = dp[i - 1][j][k]; if(k > 0)dp[i][j][k] += dp[i - 1][j + 1][k - 1] * (j + 1); if(j > 0)dp[i][j][k] += dp[i - 1][j - 1][k] * (m- j - k + 1); if(j > 1)dp[i][j][k] += dp[i - 1][j - 2][k] * c(m - j - k + 2); if(k > 1)dp[i][j][k] += dp[i - 1][j + 2][k - 2] * c(j + 2); if(k > 0 && j > 0)dp[i][j][k] += dp[i - 1][j][k - 1] * j * (m - j - k + 1); dp[i][j][k] %= MOD; } } long long ans = 0; for(int i = 0;i <= m;i++) for(int j = 0;j + i <= m;j++) ans = (ans + dp[n][i][j]) % MOD; cout << ans; return 0; }