[luogu2051][bzoj1801][AHOI2009]chess中国象棋【动态规划】
题目描述
这次小可可想解决的难题和中国象棋有关,在一个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个,使得没有任何一个炮可以攻击另一个炮. 请问有多少种放置方法,中国像棋中炮 ...
随机推荐
- JSLinux
JSLinuxhttps://bellard.org/jslinux/vm.html?url=https://bellard.org/jslinux/win2k.cfg&mem=192& ...
- python读取文件内的IP信息 练习
代码如下: #导包 import fileinput import re def readArw(): for line in fileinput.input(r"G:/raw.txt&qu ...
- 解决多人开发时使用window.onload的覆盖问题
通用型小函数:解决多人开发时,同时使用window.onload事件所出现的后面的window.onload函数覆盖前面一个window.onload函数的问题. function addLoadEv ...
- C#的类型推断发展史
前言:随着C#的版本升级,C#编译器的类型推断功能也在不断的升级以适应语言进化过程中的变化,并为这个过程做了相应的优化. 隐式类型的数组 在C#1和C#2中,作为变量声明和初始化的一部分,初始化数组的 ...
- Appscanner实验还原code3
# Author: Baozi #-*- codeing:utf-8 -*- import _pickle as pickle from sklearn import ensemble import ...
- 如何使用apache自带的ab压力测试工具
ab是apache自带的一个很好用的压力测试工具,当安装完apache的时候,就可以在bin下面找到ab 1 我们可以模拟100个并发用户,对一个页面发送1000个请求 ./ab -n1000 -c1 ...
- sql 用户相关命令
查看所有用户 select distinct concat(user, '@', host,';') as userList from mysql.user; select #查找 distinct ...
- 表单中input name属性有无[]的区别
1 input数组 如下一个表单: <input type="text" name="username[]" value="Jason" ...
- Freemake Video Converter视频转换软件下载地址及去广告
下载地址:http://download.freemake.net/FreemakeOriginals2/LS/FreemakeVideoConverterFull.exe 去片头及片尾广告:删除安装 ...
- kubernetes 简单service的例子
首先建一个Deployment: apiVersion: apps/v1beta1 kind: Deployment metadata: name: httpd spec: replicas: 3 t ...