Bzoj 4806 炮 (dp)
题目描述
众所周知,双炮叠叠将是中国象棋中很厉害的一招必杀技。炮吃子时必须隔一个棋子跳吃,即俗称"炮打隔子"。
炮跟炮显然不能在一起打起来,于是rly一天借来了许多许多的炮在棋盘上摆了起来……他想知道,在N×M的矩形
方格中摆若干炮(可以不摆)使其互不吃到的情况下方案数有几种。
棋子都是相同的。
输入输出格式
输入格式:一行,两个正整数N和M。
N<=100,M<=100
输出格式:一行,输出方案数mod 999983。
输入输出样例
输入样例#1:
1 3
输出样例#1:
zZhBr
7
分析 :
显然一行一列只能放2个或以下棋子, 否则会相互攻击;
明显的DP;
定义f[i][j][k] , 表示前i行, 有j列是放了1个, k列放了两个;
转移方程 :
f[i][j][k] += f[i-1][j][k] 什么都没放;
f[i][j][k] += f[i-1][j-1][k] * (m - (j - 1) - k) 在没放棋子的一列放了一个;
f[i][j][k] += f[i-1][j+1][k-1] * (j + 1) 在有一个棋子的一列放了一个, 变成了两个;
f[i][j][k] += f[i-1][j-2][k] * C(m - (j - 2) - k, 2) 在没放棋子的两列分别放两个棋子;
f[i][j][k] += f[i-1][j][k-1] * j * (m - (j - 1) - k) 直接在没放棋子的一列放上两个棋子;
f[i][j][k] += f[i-1][j+2][k-2] * C(j + 2, 2) 在放了一个棋子的两列风别放上一个;
记得取模;
代码奉上
// By zZhBr #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int p = 999983; #define int long long int n, m; int f[105][105][105]; int ans; signed main() { cin >> n >> m; f[0][0][0] = 1; for(register int i = 1 ; i <= n ; i ++) { for(register int j = 0 ; j <= m ; j ++) { for(register int k = 0 ; k <= m - j ; k ++) { f[i][j][k] = (f[i][j][k] + f[i-1][j][k]) % p; if(j - 1 >= 0) f[i][j][k] = (f[i][j][k] + f[i-1][j-1][k] * (m - (j - 1) - k)) % p; if(k - 1 >= 0) f[i][j][k] = (f[i][j][k] + f[i-1][j+1][k-1] * (j + 1))% p; if(j - 2 >= 0) { int t = m - (j - 2) - k; f[i][j][k] = (f[i][j][k] + f[i-1][j-2][k] * (t * (t - 1)) / 2) % p; } if(k - 2 >= 0) f[i][j][k] = (f[i][j][k] + f[i-1][j+2][k-2] * (j + 1) * (j + 2) / 2) % p; if(k - 1 >= 0) f[i][j][k] = (f[i][j][k] + f[i-1][j][k-1] * j * (m - (j - 1) - k)) % p; if(i == n) ans = (ans + f[i][j][k]) % p; //printf("n == %lld, f[i][j][k] == %lld\n", i, f[i][j][k]); } } } cout << ans << endl; return 0; }
提交地址 : https://www.lydsy.com/JudgeOnline/problem.php?id=4806 ;