是道好題.jpg√html
戳個連接吧:Luogu P2051 [AHOI2009]中國象棋c++
首先必定不要被你谷的標籤迷惑數組
這道題狀壓撐死算是拿部分分的一個方法(並且即便數據範圍很小咱也沒想明白怎麼狀壓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
考慮初始只有一行的狀態:
顯然第一行有三种放法:
這也就是初始的狀態(反正咱是這麼寫的
而後考慮轉移:
對於\(dp[i][x][y]\)
有如下n種可能:
固然以上全部都不能數組越界的,因此要判斷x和y的值是否>0,>1;
另外在循環時,由於一共只有m列,所以i+j的值要<=m,因此能夠直接將y從m-x開始循環到0而不用從0~m都循環一遍;
#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; }