计数dp

  计数类的$dp$没做过几个,所以之前都放到"思维"标签下了,后来发现原来这属于一类问题啊...搬过来了.

  

  管道取珠:https://www.lydsy.com/JudgeOnline/problem.php?id=1566

  题意概述:

  

  有这样的两根管道接在一起,上边有$n$个珠子,下边有$m$个,每次可以从上或下取出一个(向右滑出去),只会有两种颜色的珠子,求不同的取珠序列的总数的平方和.(只要两个取出序列的珠子颜色都相同就视为相同的序列,不用考虑上下).$n,m<=300$

  $shallwe$:完全可以作为$NOIP$ $D2T1$.

  既然要求平方和,是不是要先求每种序列的方案数再平方求和呢?然而这样做是做不出来的.

  出题人怎么说就怎么做是没有前途的...这里有一个挺神奇的转化:视为两个人各拿一个这样的管道取珠,当两个人取到的方案完全相同时答案$+1$,考虑这样做为什么是对的.比如取出某个序列有$a$种方法,那么这第一个人从这$a$种中任取一种,第二个人就有$a$种方法取出与之相同的序列,也就是平方了.太奇妙了.

  这个转移其实非常简单,考虑一个最普通的:$dp[i][j][k][z]$表示第一个人在上边选了$i$个,下面选了$j$个,第二个人在上边选了$k$个,下边选了$z$个的方案数.既然序列要相同,那两个人取的珠子总数应该是相同的,可以直接删掉任意一维.现在时间复杂度已经能过了,但是空间复杂度又过不了了...显然需要用一个滚动数组,但是这样的状态没有办法滚动,所以重新设计:$dp[s][i][k]$,为了方便理解直接沿用了上边的下标名称,表示共取了$s$个,第一个人在上边取了$i$个,第二个人在上边取了$j$个的方案数.此时可以发现$s$每次只会增加$1$,滚动掉.

  

 # include <cstdio>
# include <iostream>
# include <queue>
# include <cstring>
# include <string>
# define R register int
# define ll long long
# define mod using namespace std; const int maxn=;
int n,m;
char s[maxn];
int a[maxn],b[maxn];
int dp[][maxn][maxn]; int ad (int a,int b)
{
a=a+b;
if(a>=mod) a-=mod;
return a;
} int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+);
for (R i=n;i>=;--i)
if(s[i]=='A') a[n-i+]=;
scanf("%s",s+);
for (R i=m;i>=;--i)
if(s[i]=='A') b[m-i+]=;
dp[][][]=;
for (R s=;s<=n+m;++s)
{
int las=s&;
int no=las^;
memset(dp[no],,sizeof(dp[no]));
for (R i=;i<=n;++i)
for (R k=;k<=n;++k)
{
int j=s-i,z=s-k,x=dp[las][i][k];
if(!x) continue;
if(a[i+]==a[k+]) dp[no][i+][k+]=ad(dp[no][i+][k+],x);
if(a[i+]==b[z+]) dp[no][i+][k]=ad(dp[no][i+][k],x);
if(b[j+]==a[k+]) dp[no][i][k+]=ad(dp[no][i][k+],x);
if(b[j+]==b[z+]) dp[no][i][k]=ad(dp[no][i][k],x);}
}
printf("%d",dp[(n+m)&][n][n]);
return ;
}

管道取珠

  中国象棋:https://www.lydsy.com/JudgeOnline/problem.php?id=1801

  题意概述:在一个$n \times m$的棋盘上放任意多个炮,使得它们不能相互攻击,求方案数.$n,m<=100$

  前置知识:炮的攻击方式

  一开始以为是炮可以攻击同一行和同一列,然而并不是..."马后炮"了解一下?

  转化题目:每一行,每一列不能出现超过$2$个棋子的方案数.

  一开始想到了状压,但是状压压不了$100$位,即使用$bitset$强行压起来了也没法转移.后来发现具体是哪一行有两个棋子或者一个棋子并不重要,我们只需要知道有多少列有两个、一个、没有棋子就可以转移了,因为每列的本质其实是一样的。$dp[i][j][k]$表示前$i$行中$j$列有一个棋子,$k$列有两个棋子的方案数,转移比较麻烦.

  ·如果这一行一个都不放,可以直接继承上面的状态;

  ·如果放一个,可以放到已有一个的地方,那么这一行数量加一,方案数为$(j+1) \times dp[i-1][j+1][k-1]$,也可以放到本来为空的地方$dp[i-1][j-1][k] \times (m-(j-1)-k)$;

  ·如果放两个,可以都放到已有一个的地方$dp[i-1][j+2][k-2] \times c[j+2][2]$,也可以都放到空位上$dp[i-1][j-2][k] \times c[m-(j-2)-k][2]$,也可以一个放到已有一个的地方,另一个放到空位上$dp[i-1][j][k-1] \times (m-j-(k-1)) \times j$。($c$数组中是组合数)

  

 # include <cstdio>
# include <iostream>
# include <cstring>
# define ll long long
# define R register int
# define mod using namespace std; int n,m,ans;
long long dp[][][];
int c[][]; int ad (int i,int j,int k)
{
long long a=;
a=dp[i-][j][k]%mod;
if(j) a=(a+dp[i-][j-][k]*(m-(j-)-k))%mod;
if(k) a=(a+(dp[i-][j+][k-]*(j+)))%mod;
if(j>=) a=(a+(dp[i-][j-][k]*c[m-(j-)-k][]))%mod;
if(k) a=(a+(dp[i-][j][k-]*(m-j-(k-))%mod*j))%mod;
if(k>=) a=(a+(dp[i-][j+][k-]*c[j+][]))%mod;
return a;
} int main()
{
scanf("%d%d",&n,&m);
dp[][][]=;
c[][]=;
for (R i=;i<=m+;++i)
{
c[i][]=;
for (R j=;j<=;++j)
c[i][j]=(c[i-][j-]+c[i-][j])%mod;
}
for (R i=;i<=n;++i)
for (R j=;j<=m;++j)
for (R k=;k+j<=m;++k)
dp[i][j][k]=ad(i,j,k);
for (R i=;i<=m;++i)
for (R j=;j+i<=m;++j)
ans=(ans+dp[n][i][j])%mod;
printf("%d",ans);
return ;
}

中国象棋

计数dp的更多相关文章

  1. HDU5800 To My Girlfriend 背包计数dp

    分析:首先定义状态dp[i][j][s1][s2]代表前i个物品中,选若干个物品,总价值为j 其中s1个物品时必选,s2物品必不选的方案数 那么转移的时候可以考虑,第i个物品是可选可可不选的 dp[i ...

  2. CodeForces 176B Word Cut (计数DP)

    Word Cut Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit St ...

  3. [DP之计数DP]

    其实说实在 我在写这篇博客的时候 才刚刚草了一道这样类型的题 之前几乎没有接触过 接触过也是平时比赛的 没有系统的做过 可以说0基础 我所理解的计数dp就是想办法去达到它要的目的 而且一定要非常劲非常 ...

  4. HDU4815/计数DP

    题目链接[http://acm.hdu.edu.cn/showproblem.php?pid=4815] 简单说一下题意: 有n道题,每到题答对得分为a[ i ],假如A不输给B的最小概率是P,那么A ...

  5. HDU 6377 度度熊看球赛 (计数DP)

    度度熊看球赛 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  6. [SDOI2010]地精部落[计数dp]

    题意 求有多少长度为 \(n\) 的排列满足 \(a_1< a_2> a_3 < a_4 \cdots\) 或者 $a_1> a_2 < a_3 > a_4\cdo ...

  7. 【BZOJ】2111: [ZJOI2010]Perm 排列计数 计数DP+排列组合+lucas

    [题目]BZOJ 2111 [题意]求有多少1~n的排列,满足\(A_i>A_{\frac{i}{2}}\),输出对p取模的结果.\(n \leq 10^6,p \leq 10^9\),p是素数 ...

  8. 【AtCoder】AGC022 F - Leftmost Ball 计数DP

    [题目]F - Leftmost Ball [题意]给定n种颜色的球各k个,每次以任意顺序排列所有球并将每种颜色最左端的球染成颜色0,求有多少种不同的颜色排列.n,k<=2000. [算法]计数 ...

  9. 【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

    [题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一 ...

随机推荐

  1. Visual studio 2017 Installer 打包.netframework

    前几步和网上其他教程一样的.主要是把.net framework 打包进安装程序里,如果选的是“从与我的应用程序相同的位置下载系统必备组件”,会提示 ERROR: 要在“系统必备”对话框中启用“从与我 ...

  2. RabbitMQ学习系列一:windows下安装RabbitMQ服务

    Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装Rabbit MQ的前提是安装Erlang. 第一步下载erlang:http://www.erlang.org/download ...

  3. String 字符串相加比较

    String 字符串相加 对比 public static void main(String[] args) { String a = "helloword"; final Str ...

  4. Java基础——工厂模式

    通过学习,一句话概括Java工厂模式的特点——通过建立一个工厂来创建对象,不必关心构造对象实例能不能被实例化啊等诸多细节和复杂过程. 工厂模式呢?就像我们从劳动密集型社会转型到技术密集型社会.打个比方 ...

  5. Java - 生产者消费者问题

    Java多线程系列--“基础篇”11之 生产消费者问题 概要 本章,会对“生产/消费者问题”进行讨论.涉及到的内容包括:1. 生产/消费者模型2. 生产/消费者实现 转载请注明出处:http://ww ...

  6. RocketMQ 概述

    Rocket 火箭 MQ的作用:同步转异步(异步解耦). 难点:如何确保消息一定被消费,而且仅消费一次. 1.消息架构:生产者.服务器.消费者.路由发现. 2.消息顺序:严格按照消息到达服务器的顺序进 ...

  7. Hbuilder配置识别逍遥安卓模拟器

    最近在公司,需要开发一款智能家居APP,用到MUI框架,首先就是环境搭建,写好的APP页面,怎么跑在模拟器上运行? 我的开发环境: HBuilder版本:9.0.2.201803061935 逍遥安卓 ...

  8. php面向对象高级-魔术方法与迭代器

    1,魔术方法__set与__get, __call >这些魔术方法,将在相关的属性或者方法不存在时调用 >函数原型 .function __set( $property, $value ) ...

  9. Entity FrameWork(实体框架)是以ADO.NET Entity FrameWork ,简称为EF

    Entity FrameWork(实体框架)是以ADO.NET Entity FrameWork ,简称为EF Entity FrameWork的特点 1.支持多种数据库(MSSQL.Oracle.M ...

  10. sql 获取批处理信息的脚本(优化器在处理批处理时所发生的优化器事件)

    --获取批处理信息的脚本(优化器在处理批处理时所发生的优化器事件) SET NOCOUNT ON; DBCC FREEPROCCACHE; --清空过程缓存 GO --使用tempdb..Optsta ...