放 棋 子 放棋子 放棋子
题目描述见链接 .
正 解 部 分 \color{red}{正解部分} 正解部分
每个棋子都可以占据一行和一列, 且同一行同一列不能出现相同的棋子, 考虑 一个一个棋子放 不如 考虑 一种一种棋子放 ,
设 F [ i , j , k ] F[i, j, k] F[i,j,k] 表示前 k k k 种棋子占据了 i i i 行 j j j 列的方案数, g [ i , j , k ] g[i, j, k] g[i,j,k] 表示 k k k 个棋子占据 i i i 行 j j j 列的 方案数,
则 F [ i , j , k ] = ∑ l = 0 i − 1 ∑ r = 0 j − 1 F [ l , r , k − 1 ] ( N − l i − l ) ( M − r j − r ) g [ i − l , j − r , a [ k ] ] ( i − l ) ( j − r ) ≤ a [ k ] F[i, j, k] = \sum\limits_{l=0}^{i-1} \sum\limits_{r=0}^{j-1} F[l, r, k-1] \begin{pmatrix} N-l \\ i-l \end{pmatrix} \begin{pmatrix} M-r \\ j-r \end{pmatrix} g[i-l, j-r, a[k]]\ \ \ \ \ \ \ \ \ \ (i-l)(j-r) \le a[k] F[i,j,k]=l=0∑i−1r=0∑j−1F[l,r,k−1](N−li−l)(M−rj−r)g[i−l,j−r,a[k]] (i−l)(j−r)≤a[k]
g [ i , j , k ] g[i, j, k] g[i,j,k] 使用 容斥 递推, 使用 总方案数 减去 填不满的方案数 即为 合法方案数 .
g [ i , j , k ] = ( i × j k ) − ∑ l = 1 i ∑ r = 1 j g [ l , r , k ] ( i l ) ( j r ) g[i, j, k] = \begin{pmatrix} i\times j \\ k \end{pmatrix} - \sum\limits_{l=1}^i \sum\limits_{r=1}^j g[l, r,k] \begin{pmatrix} i \\ l \end{pmatrix} \begin{pmatrix} j \\r \end{pmatrix} g[i,j,k]=(i×jk)−l=1∑ir=1∑jg[l,r,k](il)(jr)
注意 g [ i , j , k ] g[i, j, k] g[i,j,k] 转移时要避免从自身转移过来, 即 l l l r r r 不能同时等于 i i i j j j .
实 现 部 分 \color{red}{实现部分} 实现部分
#include<bits/stdc++.h>
#define reg register
const int maxn = 35;
const int mod = 1e9 + 9;
int N;
int M;
int K;
int Mx;
int a[maxn];
int F[maxn][maxn][maxn];
int g[maxn][maxn][maxn];
int C[maxn*maxn][maxn*maxn];
int main(){
scanf("%d%d%d", &N, &M, &K);
for(reg int i = 1; i <= K; i ++) scanf("%d", &a[i]);
C[0][0] = 1;
for(reg int i = 1; i <= N*M; i ++){
C[i][0] = 1;
for(reg int j = 1; j <= i; j ++) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;
}
F[0][0][0] = 1;
for(reg int k = 1; k <= K; k ++)
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= M; j ++){
if(i*j < a[k]) continue ;
int &t = g[i][j][k]; t = C[i*j][a[k]];
for(reg int l = 1; l <= i; l ++)
for(reg int r = 1; r <= j; r ++){
if(l == i && r == j) continue ;
t = (t - 1ll*g[l][r][k]*C[i][l]%mod*C[j][r]%mod + mod) % mod;
}
}
for(reg int k = 1; k <= K; k ++)
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= M; j ++)
for(reg int l = 0; l < i; l ++)
for(reg int r = 0; r < j; r ++){
if((i-l)*(j-r) < a[k]) continue ;
int &t = F[i][j][k];
t = (t + 1ll*F[l][r][k-1]*C[N-l][i-l]%mod*C[M-r][j-r]%mod*g[i-l][j-r][k]) % mod;
}
int Ans = 0;
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= M; j ++) Ans = (Ans + F[i][j][K]) % mod;
printf("%d\n", Ans);
return 0;
}

博客探讨了CQOI2011中放棋子问题的解决方案,使用动态规划方法进行求解。通过F[i,j,k]表示前k种棋子占据i行j列的方案数,g[i,j,k]表示k个棋子占据i行j列的合法方案数,通过递推公式分别计算这两个量。最后介绍了具体的实现部分。"
84346569,8194746,C语言编程练习题解析,"['C语言', '编程题', '算法', '数据结构']
1879

被折叠的 条评论
为什么被折叠?



