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 }
View Code

 

      

posted @ 2025-09-04 20:19  Guo5919  阅读(5)  评论(0)    收藏  举报