点此看题面

大致题意: 让你在一张\(N*M\)的棋盘上摆放炮,使其无法互相攻击,问有多少种摆法。

辟谣

听某大佬说这是一道状压\(DP\)题,于是兴冲冲地去做,看完数据范围彻底懵了:\(N≤100\)!这么大的数据范围压死你!

好吧,其实这就是一道普通的\(DP\),与状压没有任何关系。

其实状压可以用来骗分,能得50。

考虑性质

对于这种题目,第一步肯定是考虑有没有什么比较重要的性质。

考虑炮的攻击方法,应该不难发现,每一行、每一列放的炮的数量不能超过\(2\)

保证每行不超过\(2\),应该很简单。

那么如何处理每列不超过\(2\)呢?这时就不难想到用\(f_{i,j,k}\)来表示当前处理到第\(i\)行,有\(j\)列有\(1\)个炮,\(k\)列有\(2\)个炮时的方案数

这样一来,应该就有一个比较清晰的思路了。

接下来,就是无比烦人的分类讨论

分类讨论

不难发现,这题的状态有很多转移方式,因此就需要用到分类讨论。

  • 第一种情况: 这一行什么棋子也不放。

    直接将\(f_{i,j,k}\)加上\(f_{i-1,j,k}\)即可。

  • 第二种情况: 在没有放过炮的一列上放一个炮。

    比较显然应从状态\((i-1,j-1,k)\)转移过来。

    \(∵\)在转移之前有\((m-i-j+1)\)列是没有放过炮的,

    \(∴\)放炮的方式共有\((m-i-j+1)\)种,

    \(∴\)将\(f_{i,j,k}\)加上\((m-i-j+1)*f_{i-1,j-1,k}\)。

  • 第三种情况: 在没有放过炮的两列上各放一个炮。

    此时的状态应从状态\((i-1,j-2,k)\)转移过来。

    \(∵\)在转移之前有\((m-i-j+2)\)列是没有放过炮的,

    \(∴\)放炮的方式共有\(C_{m-j-k+2}^2\)种,即\(\frac{(m-j-k+1)*(m-j-k+2)}2\)种,

    \(∴\)将\(f_{i,j,k}\)加上\(\frac{(m-j-k+1)*(m-j-k+2)}2*f_{i-1,j-2,k}\)。

  • 第四种情况: 在放过一个炮的一列上放一个炮。

    此时的状态应从状态\((i-1,j+1,k-1)\)转移过来。

    \(∵\)在转移之前有\((j+1)\)列是放过一个炮的,

    \(∴\)放炮的方式共有\((j+1)\)种,

    \(∴\)将\(f_{i,j,k}\)加上\((j+1)*f_{i-1,j+1,k-1}\)。

  • 第五种情况: 在放过一个炮的两列上各放一个炮。

    此时的状态应从状态\((i-1,j+2,k-2)\)转移过来。

    \(∵\)在转移之前有\((j+2)\)列是放过一个炮的,

    \(∴\)放炮的方式共有\(C_{j+2}^2\)种,即\(\frac{(j+1)*(j+2)}2\)种,

    \(∴\)将\(f_{i,j,k}\)加上\(\frac{(j+1)*(j+2)}2*f_{i-1,j+2,k-2}\)。

  • 第六种情况: 在没有放过炮的一列和放过一个炮的一列上各放一个炮。

    此时的状态应从状态\((i-1,j,k-1)\)转移过来。

    \(∵\)在转移之前有\((m-j-k+1)\)列是没有放过炮的,有\(j\)列是放过一个炮的,

    \(∴\)放炮的方式共有\((m-j-k+1)*j\)种,

    \(∴\)将\(f_{i,j,k}\)加上\((m-j-k+1)*j*f_{i-1,j,k-1}\)。

大致就是这\(6\)种情况了,不过取模之类的细节还是需要自己注意一下。

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define ten(x) (((x)<<3)+((x)<<1))
#define MOD 9999973
#define N 100
using namespace std;
int n,m;
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class Class_DP//DP
{
private:
int f[N+5][N+5][N+5];//用f[i][j][k]来表示当前处理到第i行,有j列有1个炮,k列有2个炮时的方案数
public:
inline int GetAns()
{
register int i,j,k,lim,ans=0;
for(i=f[0][0][0]=1;i<=n;++i)//枚举行
{
for(j=lim=min(i<<1,m);~j;--j) for(k=lim-j;~k;--k)//枚举放过一个棋子和两个棋子的列数
{
f[i][j][k]=f[i-1][j][k];//第一种情况
if(j>=1) Inc(f[i][j][k],1LL*f[i-1][j-1][k]*(m-j-k+1)%MOD);//第二种情况
if(j>=2) Inc(f[i][j][k],1LL*f[i-1][j-2][k]*((m-j-k+1)*(m-j-k+2)>>1)%MOD);//第三种情况
if(k>=1) Inc(f[i][j][k],1LL*f[i-1][j+1][k-1]*(j+1)%MOD);//第四种情况
if(k>=2) Inc(f[i][j][k],1LL*f[i-1][j+2][k-2]*((j+1)*(j+2)>>1)%MOD);//第五种情况
if(j>=1&&k>=1) Inc(f[i][j][k],1LL*f[i-1][j][k-1]*(m-j-k+1)%MOD*j%MOD);//第六种情况
}
}
for(i=lim=min(n<<1,m);~i;--i) for(j=lim-i;~j;--j) Inc(ans,f[n][i][j]);//统计答案
return ans;
}
}DP;
int main()
{
F.read(n),F.read(m),F.write(DP.GetAns());
return F.end(),0;
}

【洛谷2051】[AHOI2009] 中国象棋(烦人的动态规划)的更多相关文章

  1. BZOJ1801或洛谷2051 [AHOI2009]中国象棋

    BZOJ原题链接 洛谷原题链接 这题挺难想状态的,刚看题感觉是状压,但数据\(100\)显然不可能. 注意到每行每列只能放\(0\sim 2\)个棋子,所以我们可以将这个写入状态. 设\(f[i][j ...

  2. 洛谷.2051.[AHOI2009]中国象棋(DP)

    题目链接 /* 每行每列不能超过2个棋子,求方案数 前面行对后面行的影响只有 放了0个.1个.2个 棋子的列数,与排列方式无关 所以设f[i][j][k]表示前i行,放了0个棋子的有j列,放了1个棋子 ...

  3. 洛谷2051 [AHOI2009]中国象棋

    题目链接 题意概述:n行m列棋盘放若干个棋子每行每列最多两个求方案总数,答案对9999973取模. 可以比较容易看出这是个dp,设f[i][j][k]表示前i行j列放1个棋子k列放2个棋子的方案总数. ...

  4. [洛谷P2051] [AHOI2009]中国象棋

    洛谷题目链接:[AHOI2009]中国象棋 题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法 ...

  5. 洛谷 P2051 [AHOI2009]中国象棋 解题报告

    P2051 [AHOI2009]中国象棋 题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法. ...

  6. 洛谷 P2051 [AHOI2009]中国象棋 状态压缩思想DP

    P2051 [AHOI2009]中国象棋 题意: 给定一个n*m的空棋盘,问合法放置任意多个炮有多少种情况.合法放置的意思是棋子炮不会相互打到. 思路: 这道题我们可以发现因为炮是隔一个棋子可以打出去 ...

  7. 洛谷 P2051 [AHOI2009]中国象棋

    题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是 ...

  8. 洛谷P2051 [AHOI2009]中国象棋(dp)

    题面 luogu 题解 \(50pts:\)显然是\(3\)进制状压\(dp\) \(100pts:\) 一行一行地考虑 \(f[i][j][k]\)表示前\(i\)行,有\(j\)列放了一个,有\( ...

  9. 洛谷P2051 [AHOI2009] 中国象棋(状压dp)

    题目简介 n*m的棋盘,对每行放炮,要求每行每列炮数<=2,求方案数%9999973 N,M<=100 题目分析 算法考虑 考虑到N,M范围较小,每一行状态只与前面的行状态有关,考虑状压D ...

  10. luogu 2051 [AHOI2009]中国象棋

    luogu 2051 [AHOI2009]中国象棋 真是一道令人愉♂悦丧心并框的好题... 首先"没有一个炮可以攻击到另一个炮"有个充分条件就是没有三个炮在同一行或同一列.证明:显 ...

随机推荐

  1. 解读人:李琼,Metabolic profiling of tumors, sera and skeletal muscles from an orthotopic murine model of gastric cancer associated-cachexia(胃癌相关恶病质的原位小鼠模型中肿瘤,血清和骨骼肌的代谢谱分析)

    发表时间:(2019年4月) IF:3.950 单位: 厦门大学 厦门理工大学 物种:小鼠的肿瘤,血清和骨骼肌 技术:核磁共振代谢组学分析 一. 概述:(用精炼的语言描述文章的整体思路及结果) 本研究 ...

  2. Spring @CacheEvict 无效。。。。

    使用 此 注解 时...需要 配置 它 的 key .... @CacheEvict(value = { "adminFunc", "groupFunc" }, ...

  3. Linux--7

    一.Nginx.conf主配置文件 Nginx主配置文件conf/nginx.conf是一个纯文本类型的文件,整个配置文件是以区块的形式组织的.一般,每个区块以一对大括号{}来表示开始与结束. 核心模 ...

  4. Docker从入门到实战(一)

    Docker从入门到实战(一) 一:容器技术与Docker概念 1 什么是容器 容器技术并不是一个全新的概念,它又称为容器虚拟化.虚拟化技术目前主要有硬件虚拟化.半虚拟化.操作系统虚拟化等.1.1关于 ...

  5. 01.Spring Ioc 容器

    基本概念 Spring 的 Ioc 容器,通常也称应用上下文.它包含了两个概念 Ioc 和 容器: 容器:顾名思义就是用来装东西的,在 Spring 中容器里盛放的就是各种各样的 Bean.既然装了东 ...

  6. postgresql实现插入数据返回当前的主键ID

    <insert id="addUser" parameterType="com.liupan.user"> <selectKey keyPro ...

  7. GTY's gay friends 线段树判断区间是否有相同数字

    http://acm.hdu.edu.cn/showproblem.php?pid=5172 判断一个区间是否为全排列是: 1.区间总和 = (1 + R - L + 1) * (R - L + 1) ...

  8. arch安装问题总结

    安装 archLinux 的时候遇到的一些问题,记录下来方便以后安装. 1.fcitx 在设置/etc/locale.conf文件时,中文不能写成zh_CN.utf-8,而是要写成zh_CN.utf8 ...

  9. Maven的学习资料收集--(八) 构建MyBatis项目

    在这里,写一下,怎么使用Maven构建MyBatis项目. 1. 新建一个Web项目 可以参考前面的博客 2. 修改pom.xml,添加MyBatis依赖 <project xmlns=&quo ...

  10. Device

    #import "AppDelegate.h" #import "RootViewController.h" @implementation AppDelega ...