• 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

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

    分类讨论大法好!

  • 相关阅读:
    数据库中的float在实体中对应的竟是double
    VS2010让开发者受益新功能深入解读
    .net 缩略图 高品质
    不错.net图片水印类
    [转] asp.net url重写
    进制转换方法汇总
    c#中如何TextBox中輸入的換行符存入access數據庫
    .net高品质缩略图和低品质缩略图
    SQL Server中获得EXEC后面的sql语句或者存储过程的返
    C#动态创建图像的方法
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9745747.html
Copyright © 2020-2023  润新知