题意

求区间[1,n]内含有相邻49的数。

思路

比较简单的按位DP思路。这是第一次学习记忆化搜索式的数位DP,确实比递推形式的更好理解呐,而且也更通用~可以一般化:

数位DP模板总结

int dfs(int pos, int pre, int flag, bool limit) {
if (pos == -1) return flag==target_flag;
if (!limit && ~dp[pos][pre][flag]) return dp[pos][pre][flag];
int res = 0;
int end = limit?num[i]:9;
for (int d = 0; d <= end; ++d)
res += dfs(pos-1, d, (flag, d), limit&&d==end);
return limit?res:dp[pos][pre][flag]=res;
}
其中:dp为记忆化数组;pos为当前处理串的第pos位;flag为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态target之类,for的时候枚举下);pre为前一位数字的状态(有时根据需要也可以加上后一位数字的状态,for的时候枚举下);limit表示之前的数是否是上界的前缀(即后面的数能否任意填)。 for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。 于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。 注意:不满足区间减法性质的话(如hdu 4376),不能用solve(r)-solve(l-1),状态设计会更加诡异。

这道题思路比较简单,看看递推代码就明白了,然后再看看记忆化搜索代码就更好理解了~

代码

[cpp]
//递推形式
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <set>
#include <queue>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;

const int maxn = 35;

typedef long long LL;
LL dp[maxn][3];
//dp[i][0]表示长度为i,包括49的个数
//dp[i][1]表示长度为i,没有49但是开头为9的个数
//dp[i][2]表示长度为i,没有49
void init(){
MEM(dp, 0);
dp[0][2] = 1;
REP(i, 1, 22){
dp[i][0] = (LL)dp[i-1][0]*10 + dp[i-1][1];
dp[i][1] = (LL)dp[i-1][2];
dp[i][2] = (LL)dp[i-1][2]*10 - dp[i-1][1];
}
}
LL solve(LL n){
vector <int> bit;
while(n){
bit.push_back(n%10);
n /= 10;
}
LL ans = 0;
bool flag = false;
bit.push_back(0);
for (int i = bit.size() - 1; i >= 1; i --){
ans += (LL)dp[i-1][0]*bit[i-1];
if (!flag && bit[i-1] > 4){
ans += (LL)dp[i-1][1];
}
if (flag){
ans += (LL)dp[i-1][2]*bit[i-1];
}
if (bit[i-1] == 9 && bit[i] == 4){
flag = true;
}
}
if (flag)
ans ++;
return ans;
}
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int t;
scanf("%d", &t);
init();
while(t --){
LL n;
scanf("%I64d", &n);
printf("%I64d\n", solve(n));
}
return 0;
}
[/cpp]
[cpp]
//记忆化搜索形式
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;
typedef long long LL;
typedef vector <int> VI;
typedef set <int> SETI;
typedef queue <int> QI;
typedef stack <int> SI;

VI dig;
LL dp[30][30][2];
LL dfs(int pos, int pre, int flag, int limit){
if (pos == -1) return flag;
if (!limit && dp[pos][pre][flag] != -1){
return dp[pos][pre][flag];
}
int end = limit?dig[pos]:9;
LL ret = 0;
for (int i = 0; i <= end; ++ i){
ret += dfs(pos-1, i, (i==9 && pre == 4)||flag, (i == end)&&limit);
}
if (!limit) dp[pos][pre][flag] = ret;
return ret;
}

int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int t;
scanf("%d", &t);
while(t --){
LL n;
scanf("%I64d", &n);
dig.clear();
MEM(dp, -1);
while(n){
dig.push_back(n%10);
n /= 10;
}
printf("%I64d\n", dfs(dig.size()-1, 0, 0, 1));
}
return 0;
}
[/cpp]

HDU 3555 Bomb (数位DP-记忆化搜索模板)的更多相关文章

  1. 数位dp/记忆化搜索

    一.引例 #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an  ...

  2. HDU 3555 Bomb 数位DP 入门

    给出n,问所有[0,n]区间内的数中,不含有49的数的个数 数位dp,记忆化搜索 dfs(int pos,bool pre,bool flag,bool e) pos:当前要枚举的位置 pre:当前要 ...

  3. [hihocoder 1033]交错和 数位dp/记忆化搜索

    #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1 ...

  4. 【poj1850】 Code 数位dp+记忆化搜索

    题目大意:给你一个字符串,问你这个字符串的rank,如果这个字符串不合法,请直接输出0.(一个合法的字符串是对于∀i,有c[i]<c[i+1]) 字符串s的rank的计算方式:以字符串长度作为第 ...

  5. [BZOJ3598][SCOI2014]方伯伯的商场之旅(数位DP,记忆化搜索)

    3598: [Scoi2014]方伯伯的商场之旅 Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 449  Solved: 254[Submit][Sta ...

  6. bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

    1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义 ...

  7. 1026-windy数+数位DP+记忆化搜索

    1026: [SCOI2009]windy数 题意:数位DP模板题: 目前只理解了记忆化搜索,就想练练手, ------给递推写法留一个位子 ------ 注意这道题要判断前导0的情况,1 )可以加一 ...

  8. 【每日dp】 Gym - 101889E Enigma 数位dp 记忆化搜索

    题意:给你一个长度为1000的串以及一个数n 让你将串中的‘?’填上数字 使得该串是n的倍数而且最小(没有前导零) 题解:dp,令dp[len][mod]为是否出现过 填到第len位,余数为mod 的 ...

  9. HDU 3555 Bomb 数位dp

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3555 Bomb Time Limit: 2000/1000 MS (Java/Others) Mem ...

随机推荐

  1. LA 3211

    As you must have experienced, instead of landing immediately, an aircraft sometimes waits in a holdi ...

  2. URAL 1183 Brackets Sequence(DP)

    题目链接 题意 : 给你一串由括号组成的串,让你添加最少的括号使该串匹配. 思路 : 黑书上的DP.dp[i][j] = min{dp[i+1][j-1] (sh[i] == sh[j]),dp[i] ...

  3. 2014多校第三场1005 || HDU 4891 The Great Pan(模拟)

    题目链接 题意 : 给你n行字符串,问你有多少种理解方式.有两大类的理解 (1){A|B|C|D|...}代表着理解方式可以是A,可以是B或C或者D. (2)$blah blah$,在$$这两个符号中 ...

  4. JS异步加载的三种方式

    js加载的缺点:加载工具方法没必要阻塞文档,过得js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作. 有些工具方法需要按需加载,用到再加载,不用不加载,. 默认正常 ...

  5. 快速幂取模 POJ 3761 bubble sort

    题目传送门 /* 题意:求冒泡排序扫描k次能排好序的全排列个数 数学:这里有一个反序列表的概念,bj表示在j左边,但大于j的个数.不多说了,我也是看网上的解题报告. 详细解释:http://blog. ...

  6. 叠罗汉III之推箱子

    有一堆箱子,每个箱子宽为wi,长为di,高为hi,现在需要将箱子都堆起来,而且为了使堆起来的箱子不到,上面的箱子的宽度和长度必须小于下面的箱子.请实现一个方法,求出能堆出的最高的高度,这里的高度即堆起 ...

  7. C#格式化输出

    double a = 12354.365; Console.WriteLine(string.Format("{0:f4}", a)); 输出a的四位小数

  8. linux 下Time_wait过多问题解决

    linux 下Time_wait过多问题解决 net.ipv4.tcp_syncookies = 1表示开启SYN Cookies.当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SY ...

  9. jquery plug-in DataTable API中文文档参考

    前言:最近在做一个WEB后台,无意中发现这个插件,试用了一下觉得不错,但网上关于它的资料大多不全,所以利用一些时间将其API文档翻了一下,发在园子里供大家参考.(p.s:个人E文水平很差,对着灵格斯翻 ...

  10. java三种调用方式(同步调用/回调/异步调用)

    1:同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用 2:回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口: 3:异步调用:一种类似消息或事件的机制 ...