p2051 [AHOI2009]中国象棋. (bzoj 1801)

题目描述

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

40pts

考试遇到了这个题,玄学打表得了\(40pts\)

玄学打表吼啊

xjb分析

正解竟然是个\(DP\)? 还有人说是状压\(DP\)?哪里来的状压啊!

前置知识

考虑到我们的合法状态的话,每一行每一列的炮的数量\(\le 2\)

(炮打隔重山?) 显然 如果一行或者一列有三个炮的话将会不合法.(两个炮可以互相打啊 qwq)

如何设状态?

因为每一行每一列的炮的数量\(\leq 2\)

所以我们考虑记数组去存储有几列放了一个炮,有几列放了两个炮.

我们又需要考虑转移?

因此设出状态

\(f[i][j][k]\)代表放了前\(i\)行,有\(j\)列是有一个棋子,有\(k\)列是有2个棋子的合法方案数.

这个时候我们知道全部的列数,又知道一些情况的列数.

所以我们可以求出不放棋子的列数

单步容斥:空的=全部的\(-\)合法的

空的序列\(=m-j-k\)

确定情况

  1. 我们可以在当前第\(i\)行不放棋子.
  2. 我们可以在当前第\(i\)行放一个棋子
  3. 我们可以在当前第\(i\)行放两个棋子.

接下来就需要分类讨论这些情况.

分类讨论

一.不放棋子

我们可以直接继承上面的状态.即
\[
f[i][j][k]=f[i-1][j][k]
\]

二.放一个棋子

显然我们不会选择放在有两个棋子的列.

因此存在情况如下

\[\begin{cases}放在有一个棋子的列 f[i][j][k]+=f[i-1][j+1][k-1]\times (j+1) \\\\\\ 放在没有棋子的列 f[i][j][k]+=f[i-1][j-1][k]\times (m-(j-1)-k)\end{cases}\]

解释:
放在一个棋子的列

我们在某一个有一个棋子列放置棋子,会使这一列变为有两个棋子.

即我们要得到\(f[i][j][k]\)需要在\(j+1\)个有一个棋子的列放置棋子,变为\(j\)个有一个棋子的列

而我们又会得到一个新的有两个棋子的列.因此我们之前必须有\(k-1\)个有两个棋子的列.

即\(f[i-1][j+1][k-1]\)的状态可以传递给\(f[i][j][k]\)

而我们又可以在\((j+1)\)中的任何一列放置这一个棋子.

因此我们要\(\times (j+1)\)

放在没有棋子的列

在一个没有棋子的列放置棋子,我们会得到一个新的有一个棋子的列.

即我们要从\(j-1\)得到\(j\).

而这个时候,我们有两个棋子的列的数量不会变,所以从\(k\)传递即可.

即\(f[i-1][j-1][k]\)的状态可以传递给\(f[i][j][k]\)

又因为我在空列中的任何一列放置这个棋子.

所以要$\times $\((m-(j-1)-k)\)

三.放两个棋子

这个时候情况会多一个.先请大家自己考虑一下.

这个时候存在情况如下

\[\begin{cases}一个放在有一个棋子的列,另一个放在没有棋子的列 f[i][j][k]+=f[i-1][j][k-1]\times j \times (m-j-(k-1)) \\\\ 都放在没有棋子的列 f[i][j][k]+=f[i-1][j-2][k]\times C_{m-(j-2)-k}^{2}\\\\ 都放在有一个棋子的列 f[i][j][k]+=f[i-1][j+2][k-2] \times C_{j+2}^{2}\end{cases}\]

解释
一个放在有一个棋子的列,一个放在没有棋子的列

这个时候,我们放置之后 :

一个没有棋子的列会变成一个有一个棋子的列,而一个有一个棋子的列会变成一个有两个棋子的列。

此时我们发现,

? 有一个棋子的列的数量不会变,因此第二维依旧为\(j\),

? 又因为我们会新增一个有两个棋子的列,所以我们需要从\(k-1\)转移过来.

又因为我们可以在有一个棋子的列随便放,空列随便放.

根据乘法原理,需要\(\times j \times (m-j-(k-1))\)

都放在没有棋子的列

此时我们放置之后

? 会增加两个新的有一个棋子的列.

因此我们需要从\(j-2\)转移过来.

而两个棋子的列的数量并不会改变,所以依旧为\(k\)

又因为在空列中我们随便放.

根据组合数学,需要\(\times C_{m-(j-2)-k}^{2}\)

都放在有一个棋子的列

我们放置在有一个棋子的列之后:

? 这两个有一个棋子的列都会变成有两个子的列.

? 即\(j+2\)变成\(j\),从\(k-2\)变成\(k\)

又因为这些有一个棋子的列我们随便选择.

根据组合数学,需要\(\times C_{j+2}^{2}\)

分析完毕

我们需要接下来做的就是判断边界,一定要判断!!(血的教训!

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<cstring>
#define mod 9999973
#define int long long
#define R register
using namespace std;
inline  void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,ans;
int f[108][108][108];
inline int C(int x)
{
    return ((x*(x-1))/2)%mod;
}
signed main()
{
    in(n),in(m);
    f[0][0][0]=1;
    for(R int i=1;i<=n;i++)
    {
        for(R int j=0;j<=m;j++)
        {
            for(R int k=0;k<=m-j;k++)
            {
                f[i][j][k]=f[i-1][j][k];
                if(k>=1)(f[i][j][k]+=f[i-1][j+1][k-1]*(j+1));
                if(j>=1)(f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1));
                if(k>=2)(f[i][j][k]+=f[i-1][j+2][k-2]*(((j+2)*(j+1))/2));
                if(k>=1)(f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1));
                if(j>=2)(f[i][j][k]+=f[i-1][j-2][k]*C(m-j-k+2));
                f[i][j][k]%=mod;
            }
        }
    }
    for(R int i=0;i<=m;i++)
        for(R int j=0;j<=m;j++)
            (ans+=f[n][i][j])%=mod;
    printf("%lld",(ans+mod)%mod);
}

原文地址:https://www.cnblogs.com/-guz/p/9740817.html

时间: 2024-10-08 10:39:26

p2051 [AHOI2009]中国象棋. (bzoj 1801)的相关文章

Luogu P2051 [AHOI2009]中国象棋(dp)

P2051 [AHOI2009]中国象棋 题面 题目描述 这次小可可想解决的难题和中国象棋有关,在一个 \(N\) 行 \(M\) 列的棋盘上,让你放若干个炮(可以是 \(0\) 个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子.你也来和小可可一起锻炼一下思维吧! 输入输出格式 输入格式: 一行包含两个整数 \(N\) , \(M\) ,之间由一个空格隔开.

P2051 [AHOI2009]中国象棋

题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子.你也来和小可可一起锻炼一下思维吧! 输入输出格式 输入格式: 一行包含两个整数N,M,之间由一个空格隔开. 输出格式: 总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果. 输入输出样例 输入样例#

luogu P2051 [AHOI2009]中国象棋

分析: 因为这是道dp题n<=100,第一想到O(n^3)的算法,发现每行每列最多两个炮,f(i,j,k)表示放完第i行,有j列一个炮,与k列两个炮的方案.即可递推. 详细看代码. #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const ll MOD=9999973; const int maxn=110; ll f[maxn][maxn][maxn]; /

Luogu P2051[AHOI2009]中国象棋【dp】By cellur925

题目传送门 题目大意:给定一个$n*m$的棋盘,求放三个"炮"使它们不共行也不共列的方案数.($n,m$$<=100$) 这题主要是转移比较困难,因为情况比较多,所以需要冷静大胆细心地进行分情况讨论. 首先我们还是设计出状态:设$f[i][j][k]$表示前$i$行,放1枚棋子的有$j$列,放2枚棋子的有$k$列的方案数. 我们这样思考:放几个?放在哪? 在第$i$行不放棋子.显然我们可以由$f[i-1][j][k]$转移过来. (f[i][j][k]+=f[i-1][j][k]

P2051 [AHOI2009]中国象棋[线性DP]

最近智商有点不在线.其实一直不在线. 题目 先是想用$f[i][j][k][0/1/2]$表示摆了i行时有j列空着,k列有了一个炮,且当下摆了0/1/2个的状态,转移方程写的出来但是极其繁琐.于是又设法听取评讲者题解修改状态,最后的012完全可以删去.那么仍可以表示这一行那些列摆过1个,那些列摆过0个的种类.转移时分类即可. $f[i][j][k]+=f[i-1][j][k]$ 什么都不摆 $f[i][j][k]+=(j+1)*f[i-1][j+1][k-1]$  摆1个炮 $f[i][j][k

BZOJ1801:[AHOI2009]中国象棋——题解

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

[AHOI2009]中国象棋

题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子.你也来和小可可一起锻炼一下思维吧! 输入输出格式 输入格式: 一行包含两个整数N,M,之间由一个空格隔开. 输出格式: 总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果. 输入输出样例 输入样例#

[BZOJ1801][AHOI2009]中国象棋(递推)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1801 分析: 只会50的状态压缩…… 然后搜了下题解,发现是dp 首先易得每行每列至多有2个棋子 设f[i][j][k]表示前i行中有j列放了1个棋子,有k列放了2个棋子,那么就有m-j-k列没有放棋子 那么下面考虑第i行放棋子情况和前i-1行放棋子状态的关系: 1.如果第i行不放棋子:f[i][j][k]+=f[i-1][j][k] 2.如果第i行放1个棋子 ①该棋子放在之前没有放

[luoguP2051] [AHOI2009]中国象棋(DP)

传送门 注释写明了一切 #include <cstdio> #define N 111 #define p 9999973 #define LL long long int n, m; LL ans, f[N][N][N]; //每一行和每一列可以放0/1/2个炮 //f[i][j][k]表示前i行放了1个炮的列有j个,放了2个炮的列有k个的方案数 //那么可以推出,放了0个炮的列有m-j-k个 inline int C(int x) { return x * (x - 1) / 2; } i