P2051 [AHOI2009]中国象棋(神奇的dp)

原题: https://www.luogu.org/problemnew/show/P2051

题意: n*m的棋盘,放任意数量的炮,要求没有一个跑会被吃,求放法的数量。

解析:

直接爆搜100*100是不行的,只能dp了。没想到这个dp其实挺难的,要先想好各种放法之间的关系。


对于一种放法分析,交换其任意两列或两行会得到另一种合法的放法。也就是说任意两列的位置关系不重要。

而在行不冲突(不超过2个)的情况下,列的状态只有3种:0、1、2。那么dp可以开两维(另一维用m减去前两维)存这三种状态的列数量。

怎么样保证行不冲突呢? 我们一行一行枚举就行了,一行一行做,放0、1或2个。就永远不会超过2个了。

dp[行下标 i][0数量 j][1数量 k]表示到i行为止,j列0个棋子,k列1个棋子的放法。

转移方程:

  1. 不放: d p [ i ] [ j ] [ k ] + = d p [ i 1 ] [ j ] [ k ] dp[i][j][k]+=dp[i-1][j][k]
  2. 放一个:
    1. 在0列: d p [ i ] [ j 1 ] [ k + 1 ] + = d p [ i 1 ] [ j ] [ k ] j dp[i][j-1][k+1]+=dp[i-1][j][k]*j
    2. 在1列: d p [ i ] [ j ] [ k 1 ] + = d p [ i 1 ] [ j ] [ k ] k dp[i][j][k-1]+=dp[i-1][j][k]*k
  3. 放两个:
    1. 在0列: d p [ i ] [ j 2 ] [ k + 2 ] + = d p [ i 1 ] [ j ] [ k ] C j 2 dp[i][j-2][k+2]+=dp[i-1][j][k]*C_j^2
    2. 在1列: d p [ i ] [ j ] [ k 2 ] + = d p [ i 1 ] [ j ] [ k ] C k 2 dp[i][j][k-2]+=dp[i-1][j][k]*C_k^2
    3. 0、1各一个: d p [ i ] [ j 1 ] [ k ] + = d p [ i 1 ] [ j ] [ k ] j k dp[i][j-1][k]+=dp[i-1][j][k]*j*k

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL dp[2][109][109];//滚动数组
const LL mod =9999973;
int main(){
    int n,m;cin>>n>>m;
    int f=0;
    dp[!f][m][0]=1;
    for(int i=1;i<=n;i++){
        memset(dp[f],0,sizeof dp[f]);
        for(int j=0;j<=m;j++){
            for(int k=0;j+k<=m;k++){
                dp[f][j][k]+=dp[!f][j][k];
                if(j)
                dp[f][j-1][k+1]+=dp[!f][j][k]*j;
                if(k)
                dp[f][j][k-1]+=dp[!f][j][k]*k;
                if(j>1)
                dp[f][j-2][k+2]+=dp[!f][j][k]*j*(j-1)/2;
                if(k>1)
                dp[f][j][k-2]+=dp[!f][j][k]*k*(k-1)/2;
                if(j&&k)
                dp[f][j-1][k]+=dp[!f][j][k]*j*k;
            }
        }
        for(int j=0;j<=m;j++){
            for(int k=0;j+k<=m;k++){
                dp[f][j][k]%=mod;
            }
        }
        f=!f;
    }
    LL ans=0;
    for(int j=0;j<=m;j++){
        for(int k=0;j+k<=m;k++){
            ans+=dp[!f][j][k];
        }
    }
    printf("%lld\n",ans%mod);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/87164079