zoukankan      html  css  js  c++  java
  • 【[AHOI2009]中国象棋】

    计数类dp还是要多写啊

    看上去并没有什么思路,加上被题解里状压的标签迷惑了,于是就去看了一眼题解里设计的状态

    之后就很好做了

    首先先搞明白这道题的本质,就是对于任何一行任何一列炮的个数都不能超过(2)

    我们设(dp[i][j][k])表示到了第(i)一共有(j)列的炮个数为(2),有(k)列个数为(1)的总方案数

    那么一个炮都没有放的列数自然是(m-k-j)

    之后就可以随便做了

    对于每一行我们有三种选择

    1. 不放

    2. 放一个

    3. 放两个

    之后这就是我们的核心思想了

    一共有五种转移,就是一些简单的计数原理和组合数学啦

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define re register
    #define maxn 105
    #define LL long long
    const LL P=9999973;
    const LL inv=4999987;//2在P意义下的逆元
    int n,m;
    LL dp[maxn][maxn][maxn];
    int main()
    {
    	scanf("%d%d",&n,&m);
    	dp[1][0][0]=1;
    	dp[1][0][1]=m;
    	dp[1][0][2]=m*(m-1)*inv%P;
    	for(re int i=1;i<n;i++)
    		for(re int j=0;j<=m;j++)
    			for(re int k=0;k<=m;k++)
    			{
    				if(j+k>m) continue;
    				int p=m-k-j;
    				if(!dp[i][j][k]) continue;
    				dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%P;//这一行什么都不放 
    				if(j+1<=m&&k-1>=0) 
    					dp[i+1][j+1][k-1]=(dp[i+1][j+1][k-1]+dp[i][j][k]*k%P)%P;//在原来有1个炮的列上放 
    				if(p&&k+1<=m&&j+k+1<=m) 
    					dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*p%P)%P;//在原来有0个炮的列上放 
    				if(j+2<=m&&k-2>=0) 
    					dp[i+1][j+2][k-2]=(dp[i+1][j+2][k-2]+(dp[i][j][k]*(k-1)*k%P)*inv)%P;//在两个原来有1的上放 
    				if(p>=2&&k+2<=m&&j+k+2<=m) 
    					dp[i+1][j][k+2]=(dp[i+1][j][k+2]+(dp[i][j][k]*(p-1)*p)%P*inv)%P;//在两个原来有0的上放 
    				if(p&&j+1<=m&&k&&j+1+k<=m)
    					dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*k*p%P)%P;//在一个原来是0,一个原来是1上放 
    				
    			}
    	LL ans=0;
    	for(re int i=0;i<=m;i++)
    	for(re int j=0;j<=m;j++)
    	if(i+j<=m) ans=(ans+dp[n][i][j])%P;
    	std::cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    Java多个版本共存
    在RestHighLevelClient中增加用户名密码验证
    request Body 查询
    threadLocal
    thread 中断 interrupt
    Thread 状态和创建方法
    URI Search
    解析器
    基本crud
    第4章 SQL与关系数据库基本操作
  • 原文地址:https://www.cnblogs.com/asuldb/p/10206217.html
Copyright © 2011-2022 走看看