0904
今天晚上又做了一题
https://www.luogu.com.cn/problem/P2051
以前做过八皇后,这个有点像,不过要用DP
放多少个炮是随意的,甚至可以不放
显然每行、每列最多两个炮
fa,b,c表示目前进行到a行,b行放了1个炮,c行放了2个炮,因此a-b-c行一个炮都没有放
那么状态转移方程怎么推呢?
需要极其繁杂的分类讨论,见下:
1.这一行啥都不放 f[i][j][k]=f[i-1][j][k] 显然
2.这一行放一个
(1)放在有一个炮的列 f[i][j][k]+=f[i-1][j+1][k-1]*(j+1)
这是因为放之后这一列不能再放了,因此要在j+1个列放炮,这样就变成了j个
同理的,有两个炮的列有k-1个,才能使放炮后有k个这样的列
另外,放在哪一列有j+1种可能,因此方程得以推出
(2)放在啥都没有的列 f[i][j][k]+=f[i-1][j-1][k]*(m-j+1-k)
原理大同小异,m-j+1-k其实应当是m-(j-1)-k,表示空列的数量
3.这一行放两个
(1)一个放在有炮的列,一个放在没炮的列 f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1)
显然有一个炮的列的数量不会变,有两个炮的列多了一个
根据乘法原理,目前有一个炮是j个,没炮m-j-(k-1)个,因此分别乘它们
(2)都放在有一个炮的列 f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2)
同理,只不过乘数其实是C2j+2 ,通过初赛知识容易想到
写了一个C函数,C(x)表示C2x
(3)都放在啥没有的列 f[i][j][k]+=f[i-1][j-2][k]*C(m-j-k+2);
同上
然后求和,注意随时取模
这道题感觉就是状态稍微有点难想,然后很复杂
代码如下:
1 #include<bits/stdc++.h> 2 #define MD 9999973 3 #define int long long 4 using namespace std; 5 int n,m,ans; 6 int f[101][101][101]; 7 int C(int x) 8 { 9 return ((x*(x-1))/2)%MD; 10 } 11 signed main() 12 { 13 cin>>n>>m; 14 f[0][0][0]=1; 15 for(int i=1;i<=n;i++) 16 { 17 for(int j=0;j<=m;j++) 18 { 19 for(int k=0;k<=m-j;k++) 20 { 21 f[i][j][k]=f[i-1][j][k]; 22 if(k>=1) f[i][j][k]+=f[i-1][j+1][k-1]*(j+1); 23 if(j>=1) f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1); 24 if(k>=2) f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2); 25 if(k>=1) f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1); 26 if(j>=2) f[i][j][k]+=f[i-1][j-2][k]*C(m-j-k+2); 27 f[i][j][k]%=MD; 28 } 29 } 30 } 31 for(int i=0;i<=m;i++) 32 { 33 for(int j=0;j<=m;j++) 34 { 35 (ans+=f[n][i][j])%=MD; 36 } 37 } 38 cout<<(ans+MD)%MD<<endl; 39 return 0; 40 }

浙公网安备 33010602011771号