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

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

    分类讨论大法好!

  • 相关阅读:
    Haskell Interactive Development in Emacs
    Access Java API in Groovy Script
    手工设置Eclipse文本编辑器的配色
    Color Theme of Emacs
    Gnucash的投资记录
    Special Forms and Syntax Sugars in Clojure
    Use w3m as Web Browser
    SSE指令集加速之 I420转BGR24
    【图像处理】 增加程序速度的方法
    TBB 入门笔记
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9745747.html
Copyright © 2011-2022 走看看