zoukankan      html  css  js  c++  java
  • 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

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

    分类讨论大法好!

  • 相关阅读:
    JAVA 8的新特性
    JAVA中map的分类和各自的特性
    设计模式之工厂方法模式(附实例代码下载)
    为什么重写equals还要重写hashcode??
    C# static的用法详解
    1-RadioButton控件的用法
    C#三种常用的读取XML文件的方法
    VS2017 中安装SVN
    pip安装selenium时,报错“You are using pip version 10.0.1, however version 18.0 is available.”的问题
    问题:unknown error: call function result missing 'value' 解决方法
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9745747.html
Copyright © 2011-2022 走看看