zoukankan      html  css  js  c++  java
  • Luogu P2051 [AHOI2009]中国象棋

    Luogu P2051 [AHOI2009]中国象棋

    是道好题.jpg√

    戳个链接吧:Luogu P2051 [AHOI2009]中国象棋

    SOLUTION:

    首先一定不要被你谷的标签迷惑

    这道题状压撑死算是拿部分分的一个方法(而且即使数据范围很小咱也没想明白怎么状压

    这道题数据范围显然不是可以状态压缩的范围

    还是老老实实考虑正常dp吧:

    首先N,M<=100,那么我们可以依据【五一qbxt】day3 动态规划中,大致推断这是一道(O(N^3))的做法的题(事实也是这样的

    首先分析可以知道,一行或一列中,最多有2个炮要不然肯定会互相攻击

    然后这个题的状态就感觉很难想(color{#ffffff}{不会告诉你们状态是touli题解的lz})

    我们设(dp[i][x][y])表示前i行,有x列的炮的数量为1,y列炮的数量为2(当然剩下的m-x-y列炮的数量为0)时的方案数;

    考虑初始只有一行的状态:

    显然第一行有三种放法:

    1. 不放任何炮,此时方案数为1;(dp[1][0][0]=1)
    2. 放一个炮,那么m个位置每个位置都有可能,方案数为m (dp[1][1][0]=m)
    3. 放两个炮,这样的方案数应该是(C _m^2) (dp[1][2][0]=C_m^2)

    这也就是初始的状态(反正咱是这么写的

    然后考虑转移:

    对于(dp[i][x][y])

    有以下n种可能:

    1. 第i行不放“炮“,那么(dp[i][x][y]+=dp[i-1][x][y])//有1个棋子的列和有2个棋子的列显然是不改变的
    2. 第i行放一个“炮”,那么又分两种可能:
      1. 未放这个棋子的时候,所在一列是空的。那么显然放上这一个棋子之后,炮的数量为1的列会增加1,因此第i-1列的炮的数量为1的列应该是x-1,那么当为第i-1行时,空行的数量为(m-(x-1)-y),我们可以选择任意一个空行操作, 所以(dp[i][x][y]+=dp[i-1][x-1][y]*(m-y-x+1))
      2. 未放这个棋子的时候,所在的一列已经有一个棋子了。如果放上这一个棋子,那么炮的数量为1的列会减少1,炮的数量为2的列会增加1,因此第i-1行炮的数量为1的列应该是有x+1,炮的数量为2的列应该为y-1,而符合所在一列已经有一个棋子的列数有x+1列,因此(dp[i][x][y]+=dp[i-1][x+1][y-1]*(x+1))
    3. 第i行放两个“炮”,分为三种可能:
      1. 之前的位置是空的。放上棋子后,增加了两个炮的数量为1的列,炮的数量为2的列并没有变化,而选择这两个空行的可能的方案数为(C_{m-y-x+2}^2),因此(dp[i][x][y]+=dp[i-1][x-2][y]*C_{m-y-x+2}^2)
      2. 之前的位置一个是空一个有棋子。我们(color{red}{仔细})推算一下,可以推出i-1的dp状态:(dp[i-1][x][y-1])而位置的选择为:(m-y-x+1)*x (空行数量 *炮的数量为1的列数量),那么(dp[i][x][y]+=dp[i-1][x][y-1] * (m-y-x+1) * x)
      3. 之前的位置都是有棋子的。也就是说,放上棋子之后,炮的数量为1的列减少了2,炮的数量为2的列增加了2,然后符合条件的组合有:(C_{x+2}^2),则(dp[i][x][y]+=dp[i-1][x+2][y-2]*C_{x+2}^2)

    当然以上所有都不能数组越界的,所以要判断x和y的值是否>0,>1;

    另外在循环时,因为一共只有m列,因此i+j的值要<=m,所以可以直接将y从m-x开始循环到0而不用从0~m都循环一遍;

    CODE:

    #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;
    }
    
  • 相关阅读:
    ionic + cordova+angularJs 搭建的H5 App完整版总结
    在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能
    【Web动画】SVG 线条动画入门
    闲来无聊,研究一下Web服务器 的源程序
    PHP实现RTX发送消息提醒
    关于AngularJS(1)
    项目总结12:bootstrap-select下拉框模糊搜索
    JAVA读取XML文件并解析获取元素、属性值、子元素信息
    项目总结11:Centos部署JDK+Tomcat+MySQL文档(阿里云-网易云-华为云)
    项目总结10:通过反射解决springboot环境下从redis取缓存进行转换时出现ClassCastException异常问题
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11806676.html
Copyright © 2011-2022 走看看