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;
}
相關文章
相關標籤/搜索