题目大意:给定一个$n*m$的棋盘,求放三个“炮”使它们不共行也不共列的方案数。($n,m$$<=100$)
这题主要是转移比较困难,因为情况比较多,所以需要冷静大胆细心地进行分情况讨论。
首先我们还是设计出状态:设$f[i][j][k]$表示前$i$行,放1枚棋子的有$j$列,放2枚棋子的有$k$列的方案数。
我们这样思考:放几个?放在哪?
- 在第$i$行不放棋子。显然我们可以由$f[i-1][j][k]$转移过来。
(f[i][j][k]+=f[i-1][j][k])%=moder;
- 在第$i$行放1个棋子。有两个位置可以选择(放1个棋子的列,没放过棋子的列)
- 放在之前有一个棋子的列,那么有一个棋子的列数变少,有两个棋子的列数变多。那么我们回到之前的状态,可以从$f[i-1][j+1][k-1]$转移来,而根据容斥的思想,我们有$(j+1)$个列可供选择。
if(k-1>=0) (f[i][j][k]+=f[i-1][j+1][k-1]*(j+1))%=moder;
-
- 放在之前没有棋子的列,那么有一个棋子的列数变多,之前可转移来的状态是$f[i-1][j-1][k]$。同理,我们有$(m-(j-1)-k)$个位置可以选择。
if(j-1>=0) (f[i][j][k]+=f[i-1][j-1][k]*(m-k-j+1))%=moder;
- 在第$i$行放2个棋子。
- 两个都放在不相同的没有棋子的列,那么有一个棋子的列数变多。之前可转移来的状态是$f[i-1][j-2][k]$。在空的列数中选2个,用到了组合数。
if(j-2>=0) (f[i][j][k]+=f[i-1][j-2][k]*C(m-k-j+2))%=moder;
-
- 两个都放在不相同的已有一个棋子的列,那么有一个棋子的列数变少,有两个棋子的列数变多。之前可转移来的状态是$f[i-1][j+2][k-2]$。同样要用到组合数。
if(k-2>=0) (f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2))%=moder;
-
- 两个棋子,一个放在已有一个棋子的列,一个放在没有棋子的列,那么有一个棋子的列数减一再加一相当于没变,有两个棋子的列数增多。并运用乘法原理。
if(k-1>=0) (f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1))%=moder;
列出了转移方程,我们的代码也就写完了(雾)。
Code
1 #include<cstdio> 2 #include<algorithm> 3 4 using namespace std; 5 typedef long long ll; 6 const ll moder=9999973; 7 8 int n,m; 9 ll ans,f[105][105][105]; 10 11 ll C(int x) 12 { 13 return (x*(x-1))>>1; 14 } 15 16 int main() 17 { 18 scanf("%d%d",&n,&m); 19 f[0][0][0]=1; 20 for(int i=1;i<=n;i++) 21 for(int j=0;j<=m;j++) 22 for(int k=0;k<=m-j;k++) 23 { 24 (f[i][j][k]+=f[i-1][j][k])%=moder; 25 if(k-1>=0) (f[i][j][k]+=f[i-1][j+1][k-1]*(j+1))%=moder; 26 if(j-1>=0) (f[i][j][k]+=f[i-1][j-1][k]*(m-k-j+1))%=moder; 27 if(k-1>=0) (f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1))%=moder; 28 if(k-2>=0) (f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2))%=moder; 29 if(j-2>=0) (f[i][j][k]+=f[i-1][j-2][k]*C(m-k-j+2))%=moder; 30 } 31 for(int j=0;j<=m;j++) 32 for(int k=0;k<=m-j;k++) 33 (ans+=f[n][j][k])%=moder; 34 printf("%lld",ans); 35 return 0; 36 }
转移的时候我竟然想,为什么没有“两个棋子放在同一个之前没放到的列”这种情况。后来才意识到,我们每次面对的,是一行,其实是一个向量,(一维数组)。每一列只能放一颗棋子...
分类讨论大法好!
原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9745747.html
时间: 2024-11-18 22:25:30