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

    题目描述

    这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

    输入输出格式

    输入格式:

    一行包含两个整数N,M,之间由一个空格隔开。

    输出格式:

    总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

    输入输出样例

    输入样例#1:
    1 3
    输出样例#1:
    7

    说明

    样例说明

    除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。

    数据范围

    100%的数据中N和M均不超过100

    50%的数据中N和M至少有一个数不超过8

    30%的数据中N和M均不超过6

    /*
    清北学堂的老师讲过这道题目
    
    题目可以转化为,在棋盘放旗子要求每行每列都至多有两个棋子的方案数 
    
    dp[i][j][k]表示前i行  有j列放了1个 棋子,有k列放了2个棋子  那么有(列数m - j - k)列放了0个棋子
    
    然后考虑转移
    
    dp[i][j][k]的转移要考虑当前列放几枚棋子
    1:
    当前列不放棋子   这时候的方案数和上一列一样
    2:当前列放1个棋子   那么就要讨论放到了的那一列原本有几枚棋子
    假设那一列没有,则dp[i][j][k]从dp[i - 1][j - 1][k] 转移而来,同时又(m - (j - 1) - k)种方法,乘法原理
    假设那一列有一个,则 dp[i][j][k]从dp[i - 1][j + 1][k - 1] 转移而来同时有j + 1种方法乘法原理
    3.当前列放两个棋子  那么要讨论放法
    假设放到了两个空行  那么从dp[i - 1][j - 2][ k]转移而来,同时又C(m - ( j - 2) - k,2 )种方法 乘法原理
    假设放到了两个有一个棋子的行  那么从 dp[i - 1][j + 2][ k - 2]转移而来,同时有C(j + 2,2)种方法 乘法原理
    假设一个放到了空行,一个放到了有一个棋子的行  那么从dp[i - 1][j][k - 1]转移而来  同时有j * (k - 1)种方法乘法原理 
    
    最后统计所有可能的解,相加输出即可 
    */
    
    
    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #define MOD 9999973
    using namespace std;
    
    int n,m;
    long long  dp[131][131][131];
    
    int c(int x)
    {
        return (x * x - x) / 2;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        dp[0][0][0] = 1;// 这个赋初值真的不明白 
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j <= m;j++)
                for(int k = 0;j + k <= m;k++)
                {
                    /*按照上面说的来*/
                    dp[i][j][k] = dp[i - 1][j][k];
                    if(k > 0)dp[i][j][k] += dp[i - 1][j + 1][k - 1] * (j + 1);
                    if(j > 0)dp[i][j][k] += dp[i - 1][j - 1][k] * (m- j - k + 1);
                    if(j > 1)dp[i][j][k] += dp[i - 1][j - 2][k] * c(m - j - k + 2);
                    if(k > 1)dp[i][j][k] += dp[i - 1][j + 2][k - 2] * c(j + 2); 
                    if(k > 0 && j > 0)dp[i][j][k] += dp[i - 1][j][k - 1] * j * (m - j - k + 1);
                    dp[i][j][k] %= MOD;
                }    
        }
        long long ans = 0;
        for(int i = 0;i <= m;i++)
            for(int j = 0;j + i <= m;j++)
                ans = (ans + dp[n][i][j]) % MOD;
        cout << ans;
        return 0;
    }
  • 相关阅读:
    Can't initialize physical volume "/dev/sdb" of volume group "cinder-volumes" without -ff /dev/sdb: physical volume not initialized.
    openstack核心组件--neutron网络服务2(4)
    openstack部署cinder
    [转载]数字证书原理,公钥私钥加密原理
    [转载]签名、加密、证书的基本原理和理解
    [转载]各种加密算法比较
    [转载]对称加密DES和TripleDES
    [转载]oracle的加密和解密
    PLSQL 问题小记
    [转载]Java中的String,StringBuilder,StringBuffer三者的区别
  • 原文地址:https://www.cnblogs.com/luoyibujue/p/7663481.html
Copyright © 2011-2022 走看看