题目描述
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
题目大意
在一个n*m的棋盘中,求任意行和任意列上最多有2个棋子的方案总数。
感想
真的是一道DP好题,状态,转移方程都非常的难想,我也WA了好几发。
分析
看到题目,很明显可以发现的是,因为炮必须是要隔山打虎,那么任意列和任意行都不能有两个棋子。(其实我非常想把题目改成3个,这样难度有加大了)。
因为每一行每一列<=2,所以
定义状态:
\[f[i][j][k]\]
表示前\(i\)中,有\(j\)列有\(1\)个棋子,有\(k\)列有\(2\)个棋子有多少种方案数。
决策分\(3\)种:
第一种是放\(0\)个棋子
这种状态很明显只有转移,也就是可以从上一层直接推过来,\(j\)和\(k\)都不变。
转移方程:\(f[i][j][k]+=f[i-1][j][k]\)
第二种是放一个\(1\)个棋子:
- \(1\)颗棋子放在没有棋子的列上,那么这样会使现在多\(1\)列有\(1\)个棋子的列,那么原来比现在要少\(1\),也就是可以从\(f[i-1][j-1][k]\)转移过来,但是在原来的状态上,有\(m-(j-1)-k\)列是没有棋子的,也就是这几列上都是可以放棋子,根据乘法原理得到转移方程就是:\(f[i][j][k]+=f[i-1][j-1][k]*(m-(j-1)-k)\)。
- \(1\)颗棋子放在有1个棋子的列上,那么就会让减少原来的有\(1\)个棋子的列,增加一个\(2\)个棋子的列,也就是说原来有\(j+1\)列1个棋子的列,有\(k-1\)个2个棋子的列,得到转移方程就是\(f[i][j][k]+=f[i-1][j+1][k-1]*(j+1)\)。
第三种是放一个\(2\)个棋子:
- \(1\)颗棋子放在没有棋子的列上,\(1\)颗棋子放在有\(1\)个棋子的列上,那么很明显这样原来的状态就是\(f[i-1][j][k-1]\),因为有一列从\(0\)变成了\(1\),有一列从\(1\)变成了\(2\),就相当于是一列从0变成了2,那么转移方程就是:\(f[i][j][k]+=f[i-1][j][k-1]*(m-j-(k-1))\)。
- \(2\)颗棋子都放在没有棋子的列上,而且两个棋子在不同的列上,那么原来就有\(j-2\)列是只有\(1\)个棋子,可得原来的状态就是\(f[i-1][j-2][k]\),因为不能有在同一列上,那么需要对剩余没有棋子的列进行排列组合,剩下来的点数为\(m-(j-2)-k\),取两个那么就是\(C^{2}_{m-(j-2)-k}\),得到转移方程:\(f[i][j][k]+=f[i-1][j-2][k]*C^{2}_{m-(j-2)-k}\)。
- \(2\)颗棋子都放在有一个棋子的列上,而且两个棋子不在同一列,那么现在减少了\(2\)个有\(1\)个棋子的列,增加了\(2\)个有\(2\)个棋子的列,推出原来的状态是\(f[i-1][j+2][k-2]\),在此根据排列组合,乘法原理得到转移方程是:\(f[i-1][j+2][k-2]*C^{2}_{j+2}\)。
综上,我们整理一下转移方程:
\[if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1))\]
\[if (j >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - (j - 1) - k))\]
\[if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - (k - 1)))\]
\[if (j >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * calc(m - (j - 2) - k))\]
\[if (k >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * calc(j + 2))\]
其中calc
就是求组合数。
ac代码
#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
#define mod 9999973
#define N 105
using namespace std;
template <typename T>
inline void read(T &x) {
x = 0; T fl = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') fl = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x *= fl;
}
ll f[N][N][N];
int n, m;
ll calc(ll x) {
return (x * (x - 1)) / 2 % mod;
}
int main() {
read(n); read(m);
f[0][0][0] = 1ll;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= m; j ++) {
for (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][j][k] + f[i - 1][j + 1][k - 1] * (j + 1)) % mod;
if (j >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - (j - 1) - k)) % mod;
if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - (k - 1))) % mod;
if (j >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * calc(m - (j - 2) - k)) % mod;
if (k >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * calc(j + 2)) % mod;
f[i][j][k] %= mod;
}
}
}
ll ans = 0;
for (int i = 0; i <= m; i ++) {
for (int j = 0; j <= m ; j ++) {
ans = (ans + f[n][i][j]) % mod;
}
}
printf("%lld\n", ans);
return 0;
}
[luogu2051][bzoj1801][AHOI2009]chess中国象棋【动态规划】的更多相关文章
-
BZOJ1801 [Ahoi2009]chess 中国象棋 动态规划
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1801 题意概括 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请 ...
-
BZOJ1801 Ahoi2009 chess 中国象棋 【DP+组合计数】*
BZOJ1801 Ahoi2009 chess 中国象棋 Description 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像棋中炮的行 ...
-
BZOJ1801 [Ahoi2009]chess 中国象棋(DP, 计数)
题目链接 [Ahoi2009]chess 中国象棋 设$f[i][j][k]$为前i行,$j$列放了1个棋子,$k$列放了2个棋子的方案数 分6种情况讨论,依次状态转移. #include <b ...
-
bzoj1801: [Ahoi2009]chess 中国象棋(DP)
1801: [Ahoi2009]chess 中国象棋 题目:传送门 题解: 表示自己的DP菜的抠脚 %题解... 定义f[i][j][k]表示前i行 仅有一个棋子的有j列 有两个棋子的有k个 的方案数 ...
-
BZOJ1801:[Ahoi2009]chess 中国象棋
Time Limit: 10 Sec Memory Limit: 64 MB Description 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置 ...
-
bzoj1801: [Ahoi2009]chess 中国象棋 dp
题意:在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧. 题解:dp[i][j][k]表示到了第i行,有j列 ...
-
BZOJ1801 [Ahoi2009]chess 中国象棋 【dp】
题目 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧. 输入格式 一行包含两个整数N,M,中间用空格分开. ...
-
bzoj1801[AHOI2009]CHESS中国象棋
题意:在棋盘上放一些炮使得它们不互相攻击.其实就是一行/一列最多放两个. 50分的数据中n,m至少有一个不超过8,比较直接的想法是对n/m中较小的一维做状态压缩,状态f[i][S1][S2]表示在前i ...
-
【BZOJ1801】[Ahoi2009]chess 中国象棋 DP
[BZOJ1801][Ahoi2009]chess 中国象棋 Description 在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像棋中炮 ...
随机推荐
-
linux0.11文件分析
在kernel包中有几个重要的文件夹和文件,他们各司其职,处理着有关内核的一些功能操作.其中文件夹有三个:blk_drv(块设备驱动),chr_drv(字符设备驱动),math(数学协处理器) 文件 ...
-
JSONP技术原理及实现
跨域问题一直是前端中常见的问题,每当说到跨域,第一浮现的技术必然就是JSONP JSONP在我的理解,它并不是ajax,它是在文档中插入一个script标签,创建_callback方法,通过服务器配合 ...
-
redis批量执行
1.首先把redis命令放在txt文件中 eg: 文件名: test.txt 内容如下: HMSET HM_001 name zhang01 age HMSET HM_002 name zhang0 ...
-
用Delphi画圆角Panel的方法(使用CreateRoundRectRgn创造区域,SetWindowRgn显示指定区域)
用Delphi画圆角Panel的方法: procedure TForm1.Button5Click(Sender: TObject);var fhr :Thandle;beginfhr:=Create ...
-
https://www.chromestatus.com/features/5093566007214080
移动端滑动报错:Unable to preventDefault inside passive event listener due to target being treated as passiv ...
-
关于DTO的理解
转自大神loveis715博文:http://www.cnblogs.com/loveis715/p/4379656.html 在一个web服务的实现中,我们常常需要访问数据库,并将从数据库中取得的数 ...
-
潭州课堂25班:Ph201805201 第四课:Linux的命令以及VIM的使用 (课堂笔记)
Linux的常用命令 引入 1:如果我们要在Linux里面实现一些比如查看文件和文件夹.新建文件夹之类的操作,应该是通过什么来实现 2:讲解Linux目录树 3:讲解Linux只区分文件名,Linux ...
-
(笔记)AT91SAM9260的启动过程详细解说
Bootstrap的启动过程 一. 说明: Bootstrap启动代码是官方提供的一级启动代码,包括汇编和C语言两部分组成.对AT91SAM9260来说编译完成后,代码长度必须小于4KB,烧写到dat ...
-
Spring ActiveMQ Caused By: javax.jms.IllegalStateException: Connection closed
根据 http://www.cnblogs.com/yshyee/p/7448808.html 进行JMS操作时,发送跟监听放到不同的项目中进行时,出现以下异常信息: org.springframew ...
-
安装hive的web界面
参考: http://blog.csdn.net/xinghalo/article/details/52433914 报错参考; http://blog.163.com/artsn@126/blog/ ...