洛谷 P2051 [AHOI2009]中国象棋(状压DP)

题目链接: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\n",ans);
30     return 0;
31 }
AC代码

 

posted @ 2020-03-14 19:35  dfydn  阅读(130)  评论(0编辑  收藏  举报