• luogu 2051 [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

    分析

    鬼知道其实我一开始想用五维

    好吧,不可行

    题解链接:题解 P2051 【[AHOI2009]中国象棋】 

    part 1 审题

    首先,整理题意:

    每行每列只能放至多两粒棋子

    part 2 状态

    好的,一格一格来是不可行了(看我的五维就知道了)

    既然状态明显以行列为单位,那状态就以行列设计

    暂定,需要存的状态:行,列,棋子数

    其实现在是真不知道要考虑什么具体状态,先分析

    part 3 转移

    说实话,格不行,那就一列一列枚举

     再考虑棋子,在当前行,我们可以选择:

    1. 不放棋子

    2. 放一粒棋子

    3. 放两粒棋子

    废话

    emmmm,好吧。

    我们放棋子的时候,会造成什么影响?

    1. 有些没棋子的列,有棋子了

    2. 有些只有一个棋子的列,有两个棋子了

    3. 有些有两个棋子的列……好吧不能在这一列放棋子了

    嗯?有什么用?用处可大了


    你想想,我们需要求的是什么?

    方案数。

    如果我们能求出这一行不放、放一粒棋子、放两粒棋子的方案数,这问题就圆满了

    怎么求?

    再看看上面的分析

    当我们不放棋子的时候,方案数就等于上一行求出的总方案数

    当我们放一粒棋子、放两粒棋子的时候,列的状态会改变

    我们该怎么表示列的状态?然后存下方案数?

    放棋子,列上的棋子数在增加,我们当然不可能存下每一列的具体状态,毕竟我们不求具体的摆放方案

    列的状态与棋子数有关,却与棋子具体在哪一行无关

    棋子具体在哪一行,或行上的棋子具体在哪一列,只对于方案数有贡献(然而这个可以用加法、乘法原理解决),对于接下来棋子的摆放无关联

    好的,状态来了

    如上,我们只存下列上的具体棋子数的列数就够了

    状态:

    dp[第i行][只有一个棋子的列有j列][有两个棋子的列有k列] = 方案数

    你问我为啥没有空列的个数?

    不就是m-j-k嘛

    好吧,怎么求方案?

    看看我们曾经的分析:

    再考虑棋子,在当前行,我们可以选择:

    1. 不放棋子

    2. 放一粒棋子

    3. 放两粒棋子

    我们放棋子的时候,会造成什么影响?

    1. 有些没棋子的列,有棋子了

    2. 有些只有一个棋子的列,有两个棋子了

    3. 有些有两个棋子的列……好吧不能在这一列放棋子了

    好的

    状态转移出来了

    分三种情况讨论,不放,放一粒,放两粒,分别计算

    不放的时候:

    放一粒棋子:

    放两粒棋子:

    这里解释一下放两粒棋子的转移:

    放空列:dp[i][j][k] += dp[i-1][j-2][k] * C2m-(j-2)-k

    放在有一个棋子的列:dp[i][j][k] += dp[i-1][j+2][k-2] * C2j+2

    一个空列,一个有棋:dp[i][j][k] += dp[i-1][j-1+1][k-1] * C1m-j-(k-1) * C1j

    求组合数:emm,通过观察,1的就是本身,2的可以这么求:

    好了,over

    代码

     1 /**************************
     2 User:Mandy.H.Y
     3 Language:c++
     4 Problem:luogu 2051
     5 Algorithm:
     6 **************************/
     7 
     8 #include<bits/stdc++.h>
     9 #define Max(x,y) ((x) > (y) ? (x) : (y))
    10 #define Min(x,y) ((x) < (y) ? (x) : (y))
    11 
    12 using namespace std;
    13 
    14 const int maxn = 105;
    15 const int mod = 9999973;
    16 
    17 int n,m;
    18 long long dp[maxn][maxn][maxn];
    19 
    20 template<class T>inline void read(T &x){
    21     x = 0;bool flag = 0;char ch = getchar(); 
    22     while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
    23     while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
    24     if(flag) x = -x;
    25 }
    26 
    27 template<class T>void putch(const T x){
    28     if(x > 9) putch(x / 10);
    29     putchar(x % 10 | 48);
    30 }
    31 
    32 template<class T>void put(const T x){
    33     if(x < 0) putchar('-'),putch(-x);
    34     else putch(x);
    35 }
    36 
    37 void file(){
    38     freopen("2051.in","r",stdin);
    39     freopen("2051.out","w",stdout);
    40 }
    41 
    42 void readdata(){
    43     read(n);read(m);
    44 }
    45 
    46 int C(int x){
    47     return x*(x-1)/2;
    48 }
    49 
    50 void work(){
    51     
    52     dp[0][0][0] = 1;
    53     
    54     for(int i = 1;i <= n; ++ i){
    55         for(int j = 0;j <= m; ++ j){
    56             for(int k = 0;k <= m - j; ++ k){
    57                 
    58                 dp[i][j][k] = dp[i - 1][j][k];//第i行不放 
    59                 
    60                 if(j > 0) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j-1][k] * (m-j+1-k)) % mod;
    61                 //第i行放一个,放在空列 
    62                 
    63                 if(k > 0) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j+1][k-1] * (j+1)) % mod;
    64                 //第i行放一个,放在有一个棋子的列 
    65                 
    66                 if(j > 1) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j-2][k] * C(m-j+2-k)) % mod;
    67                 //第i行放两个,放在空列 
    68                 
    69                 if(k > 1) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j+2][k-2] * C(j+2)) % mod;
    70                 //第i行放两个,放在有一个棋子的列 
    71                 
    72                 if(k > 0) dp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k-1] * (m-j-k+1) % mod * j) % mod;  
    73                 //注意是 * (m-j-k+1) * j 
    74                 //第i行放两个,一个放在有一个棋子的列 ,一个放空列 
    75             
    76             }
    77         }
    78     }
    79     long long ans = 0;
    80     for(int i = 0;i <= m; ++ i)
    81         for(int j = 0;j <= m - i; ++ j){//m - i
    82             ans = (ans + dp[n][i][j]) % mod;
    83         }
    84         
    85     put(ans);
    86 }
    87 
    88 int main(){
    89 //    file();
    90     readdata();
    91     work();
    92     return 0;
    93 }
    View Code

     

    非做顽石不可,哪管他敬仰暗唾
  • 相关阅读:
    NABCD项目分析
    周总结6
    移动端展示
    暑期周进度报告(四)
    暑期周进度报告(三)
    暑期周进度报告(二)
    暑期周进度报告(一)
    《人件》阅读笔记02
    周学习进度报告(2020/06/05)
    2020春季软件工程课程总结
  • 原文地址:https://www.cnblogs.com/Mandy-H-Y/p/11442727.html
Copyright © 2020-2023  润新知