Luogu P2051[AHOI2009]中国象棋【dp】By cellur925

题目传送门

题目大意:给定一个$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 }
View Code

转移的时候我竟然想,为什么没有“两个棋子放在同一个之前没放到的列”这种情况。后来才意识到,我们每次面对的,是一行,其实是一个向量,(一维数组)。每一列只能放一颗棋子...

分类讨论大法好!

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9745747.html