模板 - 动态规划 - 数位dp
#include<bits/stdc++.h>
using namespace std;
#define ll long long int a[];
ll dp[][/*可能需要的状态1*/][/*可能需要的状态2*/];//不同题目状态不同
ll dfs(int pos,int state1/*可能需要的状态1*/,int state2/*可能需要的状态2*/,bool lead/*这一位的前面是否为零*/,bool limit/*这一位是否取值被限制(也就是上一位没有解除限制)*/)
//不是每个题都要处理前导零
{
//递归边界,最低位是0,那么pos==-1说明这个数枚举完了
if(pos==-)
return ;/*这里返回1,表示枚举的这个数是合法的,那么这里就需要在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。 */
//第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
if(!limit && !lead && dp[pos][state1][state2]!=-)
return dp[pos][state1][state2];
/*常规写法都是在没有限制的条件记忆化,这里与下面记录状态对应*/
int up=limit?a[pos]:;//根据limit判断枚举的上界up
ll ans=;
//开始计数
for(int i=;i<=up;i++)//枚举,然后把不同情况的个数加到ans就可以了
{
int new_state1=???;
int new_state2=???;
/*
计数的时候用continue跳过不合法的状态,不再搜索
*/ //合法的状态向下搜索
ans+=dfs(pos-,new_state1,new_state2,lead && i==,limit && i==a[pos]);//最后两个变量传参都是这样写的
}
//计算完,记录状态
if(!limit && !lead)
dp[pos][state1][state2]=ans;
/*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/
return ans;
} ll solve(ll x)
{
//可能需要特殊处理0或者-1
if(x<=)
return ???; int pos=;
while(x)//把数位分解
{
a[pos++]=x%;//编号为[0,pos),注意数位边界
x/=;
} return dfs(pos-/*从最高位开始枚举*/,/*可能需要的状态1*/,/*可能需要的状态2*/,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
} int main()
{
memset(dp,-,sizeof(dp));
//一定要初始化为-1 ll le,ri;
while(~scanf("%lld%lld",&le,&ri))
{
printf("%lld\n",solve(ri)-solve(le-));
}
}
其实另一种计数写法对别的题目有一定的启发性,需要特别注意的是,无论哪种写法的dp结果中存的数字都是和le与ri无关的。所以在数位受限时不能取用计算过的dp值,也不能更新dp值,不受限的情况可以重复利用。
无注释版:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int a[];
ll dp[][MAXS1][MAXS2];
ll dfs(int pos,int s1,int s2,bool lead,bool limit) {
if(pos==-) {
return ?;
}
if(!limit && !lead && dp[pos][s1][s2]!=-)
return dp[pos][s1][s2];
int up=limit?a[pos]:;
ll ans=;
for(int i=; i<=up; i++) {
int ns1=op1(s1);
int ns2=op2(s2);
ans+=dfs(pos-,ns1,ns2,lead && i==,limit && i==a[pos]);
}
if(!limit && !lead)
dp[pos][s1][s2]=ans;
return ans;
} ll solve(ll x) {
if(x<=)
return ?; int pos=;
while(x) {
a[pos++]=x%;
x/=;
} return dfs(pos-,INITS1,INITS2,true,true);
} int main() {
memset(dp,-,sizeof(dp)); ll le,ri;
while(~scanf("%lld%lld",&le,&ri)) {
printf("%lld\n",solve(ri)-solve(le-));
}
}
一个更简单的模板,去掉了很多奇奇怪怪的东西,比如前导0,前导0的确应该特殊考虑而不能一概而论。
int dfs(int i, int s, bool e) {
if (i==-) return s==target_s;
if (!e && ~f[i][s]) return f[i][s];
int res = ;
int u = e?num[i]:;
for (int d = first?:; d <= u; ++d)
res += dfs(i-, new_s(s, d), e&&d==u);
return e?res:f[i][s]=res;
}
看起来清爽多了,其中:
f为记忆化数组;
i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);
s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);
e表示之前的数是否是上界的前缀(即后面的数能否任意填)。
for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。
注意:
不满足区间减法性质的话,不能用solve(r)-solve(l-1)。
看了学长的部分博客之后发现其实使用f[i][j][st]表示以j开头的i位数满足条件st的数的个数也是可以的。待更新。
模板 - 动态规划 - 数位dp的更多相关文章
- 动态规划——数位dp
通过先前在<动态规划——背包问题>中关于动态规划的初探,我们其实可以看到,动态规划其实不是像凸包.扩展欧几里得等是具体的算法,而是一种在解决问题中决策的思想.在不同的题目中,我们都需要根据 ...
- 模板:数位DP
第一次听说dp还有模板的... 当然你要是记忆化搜索的话,就可以有一些套路 这是一个伪代码: LL Dfs(LL now,限制,LL top){ if(!now) return 判断条件; if(!t ...
- 动态规划-数位dp
大佬讲的清楚 [https://blog.csdn.net/wust_zzwh/article/details/52100392] 例子 不要62或4 l到r有多少个数不含62或者4 代码 #incl ...
- 模板 - 动态规划 - 区间dp
因为昨天在Codeforces上设计的区间dp错了(错过了上紫的机会),觉得很难受.看看学长好像也有学,就不用看别的神犇的了. 区间dp处理环的时候可以把序列延长一倍. 下面是 $O(n^3)$ 的朴 ...
- [转]数位dp小记
转载自:http://blog.csdn.net/guognib/article/details/25472879 参考: http://www.cnblogs.com/jffifa/archive/ ...
- HDU 5179 beautiful number 数位dp
题目链接: hdu: http://acm.hdu.edu.cn/showproblem.php?pid=5179 bc(中文): http://bestcoder.hdu.edu.cn/contes ...
- Pair(二进制处理+数位dp)(2019牛客暑期多校训练营(第七场))
示例: 输入: 33 4 24 5 27 8 5 输出:5 7 31 题意:存在多少对<x,y>满足x&y>C或x^y<C的条件.(0<x<=A,0< ...
- 数位dp模板 [dp][数位dp]
现在才想到要学数位dp,我是不是很弱 答案是肯定的 以一道自己瞎掰的题为模板 //题: //输入数字n //从0枚举到n,计算这n+1个数中含有两位数a的数的个数 //如12930含有两位数93 #i ...
- [模板] 数位dp
数位dp 简介 数位dp指满足特定性质的数的计数, 如求 \([l, r]\) 区间内不含 \(2\) 的数的个数. 一般来说, 数位dp利用dfs解决, 有时状态数较多, 需要hash表优化. 模板 ...
随机推荐
- 我的Java开发学习之旅------>计算从1到N中1的出现次数的效率优化问题
有一个整数n,写一个函数f(n),返回0到n之间出现的"1"的个数.比如f(1)=1:f(13)=6,问一个最大的能满足f(n)=n中的n是什么? 例如:f(13)=6, 因为1, ...
- AppStore App申请审核加速
容芳志大牛一直是我学习的榜样 分类: iOS开发经验技巧2014-11-12 09:40 409人阅读 评论(0) 收藏 举报 有没有遇到上线后发现很严重的bug这种情况,修复bug后提交审核又是漫长 ...
- Spring Boot2.0之性能优化
1.JVM参数调优 针对运行效果 吞吐量 初始堆内存与最大堆尽量相同 减少垃圾回收次数 2.扫包优化: 启动优化 默认Tomcat容器改为Undertow Tomcat的吞吐量500 ...
- 分享知识-快乐自己:微服务的注册与发现(基于Eureka)
1):微服务架构 服务提供者.服务消费者.服务发现组件这三者之间的关系: 各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息. 服务消费者可从服务发现组件查询服 ...
- L84
Hospital Noise May Disrupt Patient Improvement Many who need restorative rest the most might not be ...
- onhashchange
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- MySQL当月负毛利订单明细_20161027
#当月每天负毛利订单明细 SELECT c.ID,a.city AS 城市,a.username AS 用户ID,a.订单日期,a.订单号,a.销售确认额,a.成本额,a.毛利1, CASE THEN ...
- Codefroces 762A k-th divisor 数论
Codeforces 762A 题目大意: 给定两个正整数n,k\((n \le 10^{15},k\leq10^9)\),求n的从小到大的第k个约数,无解输出-1 分析: 我们会自然而然地想到找出n ...
- PowerShell自动部署网站—(1)、安装IIS[添加角色和角色服务]
#------添加角色/角色服务------ Import-Module servermanager $features = get-windowsfeature Net-*,Telnet-*,Web ...
- DataGrid 显示选中的item
Datagrid或者listview 中想要把相应的项 滚动到当前可见的位置, 必须满足2个条件: 1) 必须去掉虚拟化 VirtualizingStackPanel.IsVirtualiz ...