$ CH5E26\times ~ $ 扑克牌: (计数类DP)



$ solution: $

唉,计数类DP总是这么有套路,就是想不到。

这道题我们首先可以发现牌的花色没有价值,只需要知道每种牌有 $ (0,4] $ 张,牌的面值也不用管,只需要知道总共有 $ (0,13] $ 种牌。然后我们就可以设出状态: $ f[i][j][p][q] $ 表示还剩 $ 1 $ 张有 $ i $ 种牌的,还剩 $ 2 $ 张牌的有 $ j $ 种,还剩 $ 3 $ 张牌的有 $ p $ 种,还剩 $ 4 $ 张牌的有 $ q $ 种,

可能有人会问,怎么想到这种方法的。事实上,这是计数类DP的重点。做题目的时候,我们往往需要去题目里挖掘性质,将题目所给条件进行转化或建模 。然后在数据范围上大胆猜想,更具经验判断。计数类DP,博主目前只会两个套路:

  1. 一个是找基准点(一般用记忆化搜索代替DP)
  2. 然后就是这一种,直接构造法,我们尤其是构造特殊排列的时候。类比这一题,我们可以用轮廓线DP来模拟构造题目所需排列,轮廓线DP里保存的就是构造到某一阶段所剩余(或用了)的元素。就像25语言那道题目。

所以这一道题我们可以枚举放牌的过程,每一次将牌放到排列末尾。当然对于不同的构造方法,轮廓线DP的实现各有千秋。这一道题之所以难,还在于我们如何让转移达到题目要求。题目里唯一一个棘手的限制就是,相邻的牌面值不同。这个情况如果我们只设四维显然不能明确表示出构造到某一阶段的具体状态。所以我们再加一维用来表示当前排列最后一张牌是从还剩四张(或三张,两张,一张)的牌中取出来的。

这个很重要,举个栗子,我现在拥有某种牌4张,我将其中一张放到排列末尾,那么我下一次放牌时如果从牌数为3三的牌中取牌就有可能抽到与我刚刚放的牌一样的牌,这个需要判断一下。(详细可以见代码,代码里转移中类似 (a-(r==2)) 这样的语句就是用来处理这种情况的)

想必大家读到这里应该已经知道如何转移了吧,我们的下一张牌(就是转移),会有四种情况:(要注意我们多加的那一维的影响)

  1. 放到排列末尾的那种牌已经只剩一张了,
  2. 还剩两张
  3. 还剩三张
  4. 还剩四张

然后,计数类DP,往往可以用记忆化搜索来带替DP,比较方便。



$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set> #define ull unsigned long long
#define ll long long
#define db double
#define rg register int using namespace std; int n;
int ch[75],g[5];
ull f[15][15][15][15][5]; inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
if(sign)return -res; else return res;
} inline ull ask(int a,int b,int c,int d,int r){
ull res=f[a][b][c][d][r]; if(res)return res;
if(a)res=res+ask(a-1,b,c,d,1)*(a-(r==2));
if(b)res=res+ask(a+1,b-1,c,d,2)*2*(b-(r==3));
if(c)res=res+ask(a,b+1,c-1,d,3)*3*(c-(r==4));
if(d)res=res+ask(a,b,c+1,d-1,4)*4*d;
return f[a][b][c][d][r]=res;
} int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
for(rg i=0;i<=4;++i)f[0][0][0][0][i]=1;
rg t,ff=0; t=qr();
while(t--){
n=qr(); string s;
for(rg i=1;i<=n;++i) cin>>s,++ch[s[0]-49];
for(rg i=1;i<=4;++i) g[i]=0;
for(rg i=0;i<=50;++i) ++g[ch[i]],ch[i]=0;
printf("Case #%d: ",++ff);
cout<<ask(g[1],g[2],g[3],g[4],0)<<endl;;
}
return 0;
}

CH5E26 扑克牌 (计数类DP)的更多相关文章

  1. 动态规划——区间DP,计数类DP,数位统计DP

    本博客部分内容参考:<算法竞赛进阶指南> 一.区间DP 划重点: 以前所学过的线性DP一般从初始状态开始,沿着阶段的扩张向某个方向递推,直至计算出目标状态. 区间DP也属于线性DP的一种, ...

  2. SDOI2010代码拍卖会 (计数类DP)

    P2481 SDOI2010代码拍卖会 $ solution: $ 这道题调了好久好久,久到都要放弃了.洛谷的第五个点是真的强,简简单单一个1,调了快4个小时! 这道题第一眼怎么都是数位DP,奈何数据 ...

  3. $Poj1737\ Connected\ Graph$ 计数类$DP$

    AcWing Description 求$N$个节点的无向连通图有多少个,节点有标号,编号为$1~N$. $1<=N<=50$ Sol 在计数类$DP$中,通常要把一个问题划分成若干个子问 ...

  4. $CF559C\ Gerald\ and\ Fiant\ Chess$ 计数类$DP$

    AcWing Description 有个$H$行$W$列的棋盘,里面有$N$个黑色格子,求一个棋子由左上方格子走到右下方格子且不经过黑色格子的方案数. $1<=H,M<=1e5,1< ...

  5. $CH5302$ 金字塔 区间$DP$/计数类$DP$

    CH Sol f[l][r]表示l到r这段区间对应的金字塔结构种数 发现是f[l][r]是可以由比它小的区间推出来的 比如已知f[l+1][k],f[k+1][r],不难想到f[l][r]+=f[l+ ...

  6. hackerrank【Lego Blocks】:计数类dp

    题目大意: 修一个层数为n,长度为m的墙,每一层可以由长度为1.2.3.4的砖块构成. 每一层都在同一个长度处出现缝隙是方案非法的,问合法的方案数有多少种 思路: 先求出总方案,再减去所有非法的方案数 ...

  7. codeforces 277.5 div2 F:组合计数类dp

    题目大意: 求一个 n*n的 (0,1)矩阵,每行每列都只有两个1 的方案数 且该矩阵的前m行已知 分析: 这个题跟牡丹江区域赛的D题有些类似,都是有关矩阵的行列的覆盖问题 牡丹江D是求概率,这个题是 ...

  8. Codeforces 9D How many trees? 【计数类DP】

    Codeforces 9D How many trees? LINK 题目大意就是给你一个n和一个h 问你有多少个n个节点高度不小于h的二叉树 n和h的范围都很小 感觉有无限可能 考虑一下一个很显然的 ...

  9. Codeforces 28C Bath Queue 【计数类DP】*

    Codeforces 28C Bath Queue LINK 简要题意:有 n 个人等概率随机进入 m 个房间,一个房间可以有多个人,第 i 个房间有 ai 个水龙头,在一个房间的人要去排队装水,他们 ...

随机推荐

  1. Json ignore on class level

    Exclude all instances of a class from serialization in Newtonsoft.Json Every custom type can opt how ...

  2. Stream 源码分析

    Stream 支持顺序和并行聚合操作的一组元素序列. 1)operations:支持在单个元素上执行的操作,流操作分为中间操作和终止操作 1-1)中间操作: 1-1-1)无状态:unordered() ...

  3. 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_10 斗地主案例(双列)_1_斗地主案例的需求分析

    之前做的斗地主的版本,没有从小到大进行排序 一个存储牌的花色,一个存储牌的序号. 放牌的容器.使用Map 再创建一个集合进行洗牌. 调用shuffer方法洗牌.生成后就是随即的索引了.

  4. robot framework :List Variables-List变量及其用法

    [转自:https://blog.csdn.net/yezibang/article/details/52692342] 这一讲我们重点来介绍List Variables-List变量及其用法. 一. ...

  5. C# datatable 导出到Excel

    datatable导出到Excel /// <summary> /// 将DataTable导出为Excel文件(.xls) /// </summary> /// <pa ...

  6. 学习《Oracle PL/SQL 实例讲解 原书第5版》----创建账户

    通过readme.pdf创建student账户. 以下用sys账户登录时都是sysdba. 一.PL/SQL 登录oracle. SYS/123  AS SYSDBA 账户名:sys:密码:123:作 ...

  7. unity editor 折叠树

    https://blog.csdn.net/e295166319/article/details/52370575 需要两个类:树节点类和界面实现类 1:树节点类(TreeNode) using Un ...

  8. chapter2

    Chapter2 Tip1 静态工厂方法代替构造器 公有的静态方法,只是一个返回类实例的静态方法. 静态工厂方法的优势: 优势一: 有名称,如果构造器本身没有正确的描述被返回的对象,具有适当名称的静态 ...

  9. Redux 中间件与函数式编程

    为什么需要中间件 接触过 Express 的同学对"中间件"这个名词应该并不陌生.在 Express 中,中间件就是一些用于定制对特定请求的处理过程的函数.作为中间件的函数是相互独 ...

  10. 本地启oracle实例服务无法重启,协议适配器错误

    今天遇到一位朋友的oracle实例服务无法起来,启动时报错: 分析的原因是可能早上服务器突然断电造成的,经过对tns的测试 经过我们讨论和诊断,最后诊断的处理方法是将实例删了重装,处理后服务恢复正常: ...