Luogu P2051 [AHOI2009]中国象棋

Luogu P2051 [AHOI2009]中国象棋

是道好题.jpg√html

戳个连接吧:Luogu P2051 [AHOI2009]中国象棋c++

SOLUTION:

首先必定不要被你谷的标签迷惑数组

这道题状压撑死算是拿部分分的一个方法(并且即便数据范围很小咱也没想明白怎么状压ui

这道题数据范围显然不是能够状态压缩的范围spa

仍是老老实实考虑正常dp吧:code

首先N,M<=100,那么咱们能够依据【五一qbxt】day3 动态规划中,大体推断这是一道\(O(N^3)\)的作法的题(事实也是这样的htm

首先分析能够知道,一行或一列中,最多有2个炮要否则确定会互相攻击blog

而后这个题的状态就感受很难想\(\color{#ffffff}{不会告诉大家状态是touli题解的lz}\)get

咱们设\(dp[i][x][y]\)表示前i行,有x列的炮的数量为1,y列炮的数量为2(固然剩下的m-x-y列炮的数量为0)时的方案数;it

考虑初始只有一行的状态:

显然第一行有三种放法:

  1. 不听任何炮,此时方案数为1;\(dp[1][0][0]=1\)
  2. 放一个炮,那么m个位置每一个位置都有可能,方案数为m \(dp[1][1][0]=m\)
  3. 放两个炮,这样的方案数应该是\(C _m^2\) \(dp[1][2][0]=C_m^2\)

这也就是初始的状态(反正咱是这么写的

而后考虑转移:

对于\(dp[i][x][y]\)

有如下n种可能:

  1. 第i行不放“炮“,那么\(dp[i][x][y]+=dp[i-1][x][y]\)//有1个棋子的列和有2个棋子的列显然是不改变的
  2. 第i行放一个“炮”,那么又分两种可能:
    1. 未放这个棋子的时候,所在一列是空的。那么显然放上这一个棋子以后,炮的数量为1的列会增长1,所以第i-1列的炮的数量为1的列应该是x-1,那么当为第i-1行时,空行的数量为(m-(x-1)-y),咱们能够选择任意一个空行操做, 因此\(dp[i][x][y]+=dp[i-1][x-1][y]*(m-y-x+1)\)
    2. 未放这个棋子的时候,所在的一列已经有一个棋子了。若是放上这一个棋子,那么炮的数量为1的列会减小1,炮的数量为2的列会增长1,所以第i-1行炮的数量为1的列应该是有x+1,炮的数量为2的列应该为y-1,而符合所在一列已经有一个棋子的列数有x+1列,所以\(dp[i][x][y]+=dp[i-1][x+1][y-1]*(x+1)\)
  3. 第i行放两个“炮”,分为三种可能:
    1. 以前的位置是空的。放上棋子后,增长了两个炮的数量为1的列,炮的数量为2的列并无变化,而选择这两个空行的可能的方案数为\(C_{m-y-x+2}^2\),所以\(dp[i][x][y]+=dp[i-1][x-2][y]*C_{m-y-x+2}^2\)
    2. 以前的位置一个是空一个有棋子。咱们\(\color{red}{仔细}\)推算一下,能够推出i-1的dp状态:\(dp[i-1][x][y-1]\)而位置的选择为:(m-y-x+1)x (空行数量 炮的数量为1的列数量),那么\(dp[i][x][y]+=dp[i-1][x][y-1] * (m-y-x+1) * x\)
    3. 以前的位置都是有棋子的。也就是说,放上棋子以后,炮的数量为1的列减小了2,炮的数量为2的列增长了2,而后符合条件的组合有:\(C_{x+2}^2\),则\(dp[i][x][y]+=dp[i-1][x+2][y-2]*C_{x+2}^2\)

固然以上全部都不能数组越界的,因此要判断x和y的值是否>0,>1;

另外在循环时,由于一共只有m列,所以i+j的值要<=m,因此能够直接将y从m-x开始循环到0而不用从0~m都循环一遍;

CODE:

#include<bits/stdc++.h>

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}
const int mod=9999973;
int n,m;
int dp[110][110][110];

long long C(int m,int k) {
    if(m<k) return 0;
    long long ans=1;
    for(int i=m;i>m-k;i--)
        ans=ans*i;
    long long fm=1;
    for(int i=k;i>=2;i--) 
        fm=fm*i;
    ans/=fm;
    return ans%mod;
}

int main() {
    n=read();
    m=read();
    dp[1][0][0]=1;
    dp[1][1][0]=m;
    dp[1][2][0]=C(m,2);
    for(int i=2;i<=n;i++) {
        for(int x=0;x<=m;x++) {
            for(int y=m-x;y>=0;y--) {
                dp[i][x][y]=dp[i-1][x][y];
                if(x>0) 
                    dp[i][x][y]=(dp[i][x][y]+dp[i-1][x-1][y]%mod*(m-y-x+1)%mod)%mod;
                if(y>0) {
                    dp[i][x][y]=(dp[i][x][y]+dp[i-1][x][y-1]%mod*(m-x-y+1)%mod*x%mod)%mod;
                    dp[i][x][y]=(dp[i][x][y]+dp[i-1][x+1][y-1]%mod*(x+1)%mod)%mod;
                }
                if(x>1) 
                    dp[i][x][y]=(dp[i][x][y]+dp[i-1][x-2][y]%mod*C(m-y-x+2,2)%mod)%mod;
                if(y>0)
                    
                if(y>1) 
                    dp[i][x][y]=(dp[i][x][y]+dp[i-1][x+2][y-2]%mod*C(x+2,2)%mod)%mod;
            }
        }
    }
    long long ans=0;
    for(int x=0;x<=m;x++) 
        for(int y=m-x;y>=0;y--) 
            ans=(ans+dp[n][x][y])%mod;
    printf("%lld",ans);
    return 0;
}