zoukankan      html  css  js  c++  java
  • BZOJ1801: [Ahoi2009]chess 中国象棋

    BZOJ1801: [Ahoi2009]chess 中国象棋

    Description

    在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.

    Input

    一行包含两个整数N,M,中间用空格分开.

    Output

    输出所有的方案数,由于值比较大,输出其mod 9999973

    Sample Input

    1 3

    Sample Output

    7

    HINT

    除了在3个格子中都放满炮的的情况外,其它的都可以.

    100%的数据中N,M不超过100
    50%的数据中,N,M至少有一个数不超过8
    30%的数据中,N,M均不超过6


    题解Here!

    一眼看去——棋盘$DP$。
    首先,每行、每列最多放俩炮,这个不用多说。
    对于$50%$的数据,直接状压$DP$对吧。
    然后我们发现,我们对于棋子的顺序与位置并不关心。
    也就是说,并不需要知道准确的状态。
    那么我们就可以把状态压缩省去了,换成讲的状态记录。
    设$dp[i][j][k]$表示当前正在填第$i+1$行,前$i$行中有$j$列放了一个棋子,有$k$列放了两个棋子。
    然后转移就是分类讨论,运用加法原理和乘法原理即可,具体可以参见代码。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 110
    #define MOD 9999973
    using namespace std;
    int n,m;
    long long ans=0,dp[MAXN][MAXN][MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline int C(int x){return x*(x-1)/2;}
    void solve(){
    	dp[0][0][0]=1;
    	for(int i=0;i<n;i++)
    	for(int j=0;j<=m;j++)
    	for(int k=0;j+k<=m;k++)
    	if(dp[i][j][k]){
    		dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%MOD;//一个棋子都不放
    		if(m-k-j>=1)//放一个,在没有棋子的那一列
    			dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k)%MOD)%MOD;
    		if(j>=1)//放一个,在有一个棋子的那一列
    			dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j%MOD)%MOD;
    		if(m-k-j>=1&&j>=1)//放两个,一个在没有棋子的列,一个在有一个棋子的列
    			dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*(m-j-k)%MOD*j%MOD)%MOD;
    		if(m-k-j>=2)//放两个,都在没有棋子的两列
    			dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*C(m-j-k)%MOD)%MOD;
    		if(j>=2)//两个,在一个棋子的列
    			dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*C(j)%MOD)%MOD;
    	}
    }
    void work(){
    	n=read();m=read();
    	solve();
    	for(int i=0;i<=m;i++)
    	for(int j=0;i+j<=m;j++)
    	ans=(ans+dp[n][i][j])%MOD;//统计方案数
    	printf("%lld
    ",ans);
    }
    int main(){
    	work();
        return 0;
    }
    

      

  • 相关阅读:
    (笔试题)镇长选举
    (笔试题)最小的非“重复的数”
    ( 笔试题)只出现一次的数
    (算法)二叉树中两个结点的最近公共父结点
    (笔试题)区间最大重叠
    (剑指Offer)面试题61:按之字形顺序打印二叉树
    (算法)Partition方法求数组第k大的数
    (剑指Offer)面试题60:把二叉树打印成多行
    整理一些不错的、网上好评的电影、电视、视频等资源地址
    个人网站/博客,建站好的域名和网站供应商网站整理
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9862190.html
Copyright © 2011-2022 走看看