题目链接:https://www.luogu.com.cn/problem/P2051
说是归在状压DP里面,但其中只有很少的状压思想,并没有什么二进制的东西。
这道题转化一下其实就是要求每一行、列上“炮”的个数不大于2。
设dp[][][]:第一维为行数,第二维为一列上有一个“炮”的列数,第三维为一列上有两个“炮”的列数。
那么可以分情况转移:
<1>直接加:dp[i+1][j][k]+=dp[i][j][k] <2>如果有一个“炮”的列数>=1:dp[i+1][j-1][k+1]+=dp[i][j][k]*j <3>如果没有“炮”的列数>=1:dp[i+1][j+1][k]+=dp[i][j][k]*(m-k-j) <4>如果有一个“炮”的列数>=2:dp[i+1][j-2][k+2]+=dp[i][j][k]*cul(j) <5>如果没有“炮”的列数>=2:dp[i+1][j-2][k+2]+=dp[i][j][k]*cul(m-k-j) <6>如果有一个“炮”的列数>=1&&如果没有“炮”的列数>=1: dp[i+1][j][k+1]+=dp[i][j][k]*j*(m-j-k) -->(dp[i+1][j-1+1][k+1])0->1=>j+1 1->2=>j-1,k+1
注意我的代码是从dp[i][j][k]转移到dp[i+1][][],所以n从0开始。
AC代码:
1 #include<cstdio> 2 typedef long long ll; 3 const ll mod=9999973; 4 int cul(int x){ 5 return x*(x-1)/2; 6 } 7 ll dp[110][110][110]; 8 int main(){ 9 dp[0][0][0]=1; 10 int n,m; 11 scanf("%d%d",&n,&m); 12 for(int i=0;i<n;i++) 13 for(int j=0;j<=m;j++) 14 for(int k=0;k+j<=m;k++){ 15 if(dp[i][j][k]==0) continue; 16 dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod; 17 if(j>=1) dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j)%mod; 18 if((m-j-k)>=1) dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k))%mod; 19 if(j>=2) dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*cul(j))%mod; 20 if((m-j-k)>=2) dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*cul(m-j-k))%mod; 21 if((m-j-k)>=1&&j>=1) dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*j*(m-j-k))%mod; 22 } 23 ll ans=0; 24 for(int j=0;j<=m;j++) 25 for(int k=0;k+j<=m;k++){ 26 ans+=dp[n][j][k]; 27 ans%=mod; 28 } 29 printf("%lld ",ans); 30 return 0; 31 }