http://acm.hdu.edu.cn/showproblem.php?pid=3555

Bomb

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 7316    Accepted Submission(s): 2551

Problem Description
The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point. Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
 
Input
The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description.
The input terminates by end of file marker.
 
Output
For each test case, output an integer indicating the final points of the power.
 
Sample Input
3
1
50
500
 
Sample Output
0
1
15

Hint

From 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499",
so the answer is 15.

 
Author
fatboy_cw@WHU
 
Source
 
Recommend
zhouzeyong   |   We have carefully selected several similar problems for you:  3554 3556 3557 3558 3559

题意:输入n,输出1~n中含有“49”的整数数量。(1 <= N <= 2^63-1

题解:

数位DP 或 记忆化搜索。

n这么大,不可能枚举。不过我们先看看按位从高到低的枚举深搜:

 ll dfs(int w, int state, bool limit) {
if(w<) return state==; int maxi=limit ? a[w] : ;
ll re=;
for(int i=; i<=maxi; i++) {
int s=state;
if(state==) {
if(i==) s=;
else if(i!=)s=;
} else if(state== && i==) s=;
re+=dfs(w-, s, limit && i==a[w]);
} return re;
}

ans=dfs(an-1,0,1)。其中state为0表示没49,state为1表示上一位是9,state为2表示有49。a[w]为n的第(w+1)位,n有an位。

可以看出这是一个非常裸的搜索,简直枚举每个数,肯定超时。

但仔细观察可以发现,有好多次递归都返回同样的结果。对,就是limit=0时,w和state固定的调用,都是返回0~w位的数随意取0~9,含有49的种类数。这样,我们可以用记忆化搜索,记录算好的f[w][state],下次调用的时候就不用算,直接返回值。

还有更快的方法,就是直接数位DP。这个实在是碉,我自己的话根本写不出来。

首先初始化还比较容易,像这样,生成的f[][]是和上面记忆化搜索的一样的:

 void init(){
memset(f,,sizeof(f));
f[][]=;
for(int i=;i<;i++){
f[i][] = (ll)f[i-][]* - f[i-][]; ///没49的 = 之前没49的这一位随便 - 之前开头是9的这一位是4
f[i][] = f[i-][]; ///开头是9的 = 之前没49的这1位是9
f[i][] = (ll)f[i-][]* + f[i-][]; ///有49的 = 之前有49的这一位随便 + 之前开头是9的这一位是4
}
}

但是统计的时候就难了,是这样的:

 ll farm(ll x) {
an=;
while(x) {
a[an++]=x%;
x/=;
}
a[an]=;
ll ans=;
bool flag=false;
for(int i=an-;i>=;i--){
ans+=(ll)f[i][]*a[i];///后面有49,这一位取0~a[i]-1(取a[i]的情况在之后的循环中计算)
if(flag) ans+=(ll)f[i][]*a[i];///前面全固定取a[i],有49了,这位0~a[i]-1,后面取没49的(后面有49的情况已经在上一句算过了)
else if(!flag && a[i]>)///前面全固定取a[i],没49,这一位能取4,后面开头为9的情况数f[i][1],则就是组成49的情况数
ans+=f[i][];
if(a[i]== && a[i+]==) flag=true;///这一位取a[i],上一位取a[i+1]时成49,标flag,日后计算
}
return ans;
}

其中for循环每层是固定比当前位更高的位为a[],即该位的边缘值,进行计算,因为那一位取不边缘的值的情况已经在那一位算过了,具体可以看上面的注释。

记忆化搜索全代码:

 //#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long ll f[][];///f[i][j],i位数,j=0是没49的,1是9开头的,2是有49的
int a[],an; ll dfs(int w, int state, bool limit) {
if(w<) return state==;
if(!limit && f[w][state]!=-) return f[w][state]; int maxi=limit ? a[w] : ;
ll re=;
for(int i=; i<=maxi; i++) {
int s=state;
if(state==) {
if(i==) s=;
else if(i!=)s=;
} else if(state== && i==) s=;
re+=dfs(w-, s, limit && i==a[w]);
} if(!limit) f[w][state]=re;
return re;
} ll farm(ll x) {
an=;
while(x) {
a[an++]=x%;
x/=;
}
return dfs(an-,,);
} int main() {
int T;
ll n;
memset(f,-,sizeof(f));
scanf("%d",&T);
while(T--) {
scanf("%I64d",&n);
printf("%I64d\n",farm(n));
}
return ;
}

数位DP全代码:

 //#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long ll f[][];///f[i][j],i位数,j=0是没49的,1是9开头的,2是有49的
int a[],an; ll farm(ll x) {
an=;
while(x) {
a[an++]=x%;
x/=;
}
a[an]=;
ll ans=;
bool flag=false;
for(int i=an-;i>=;i--){
ans+=(ll)f[i][]*a[i];///后面有49,这一位取0~a[i]-1(取a[i]的情况在之后的循环中计算)
if(flag) ans+=(ll)f[i][]*a[i];///前面全固定取a[i],有49了,这位0~a[i]-1,后面取没49的(后面有49的情况已经在上一句算过了)
else if(!flag && a[i]>)///前面全固定取a[i],没49,这一位能取4,后面开头为9的情况数f[i][1],则就是组成49的情况数
ans+=f[i][];
if(a[i]== && a[i+]==) flag=true;///这一位取a[i],上一位取a[i+1]时成49,标flag,日后计算
}
return ans;
} void init(){
memset(f,,sizeof(f));
f[][]=;
for(int i=;i<;i++){
f[i][] = (ll)f[i-][]* - f[i-][]; ///没49的 = 之前没49的这一位随便 - 之前开头是9的这一位是4
f[i][] = f[i-][]; ///开头是9的 = 之前没49的这1位是9
f[i][] = (ll)f[i-][]* + f[i-][]; ///有49的 = 之前有49的这一位随便 + 之前开头是9的这一位是4
}
} int main() {
int T;
ll n;
init();
scanf("%d",&T);
while(T--) {
scanf("%I64d",&n);
printf("%I64d\n",farm(n+));///farm(x)是算小于x的
}
return ;
}

hdu3555 Bomb (记忆化搜索 数位DP)的更多相关文章

  1. hdu_3562_B-number(记忆化搜索|数位DP)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3652 题意:给你一个n,为比n小的能整除13并数字中有13的数有多少个 题解:记忆化搜索:记dp[i] ...

  2. UVALive 4864 Bit Counting --记忆化搜索 / 数位DP?

    题目链接: 题目链接 题意:如果一个数二进制n有k位1,那么f1[n] = k,如果k有s位二进制1,那么f2[n] = f1[k] = s.  如此往复,直到fx[n] = 1,此时的x就是n的”K ...

  3. 【记忆化搜索/数位DP】zznu2175(长度为n的含有ACM的字符串)

    随机字符串 题目描述 起名字什么的最麻烦,我们来生成一些随机字符串吧 生成的字符串当然是有要求的: .长度不能超过n .字符串中仅包含大写字母 .生成的字符串必须包含字符串“ACM” ok,是不是很简 ...

  4. 记忆化搜索(DFS+DP) URAL 1223 Chernobyl’ Eagle on a Roof

    题目传送门 /* 记忆化搜索(DFS+DP):dp[x][y] 表示x个蛋,在y楼扔后所需要的实验次数 ans = min (ans, max (dp[x][y-i], dp[x-1][i-1]) + ...

  5. 记忆化搜索(DFS+DP) URAL 1501 Sense of Beauty

    题目传送门 /* 题意:给了两堆牌,每次从首部取出一张牌,按颜色分配到两个新堆,分配过程两新堆的总数差不大于1 记忆化搜索(DFS+DP):我们思考如果我们将连续的两个操作看成一个集体操作,那么这个操 ...

  6. HDU 2476 String painter(记忆化搜索, DP)

    题目大意: 给你两个串,有一个操作! 操作时可以把某个区间(L,R) 之间的所有字符变成同一个字符.现在给你两个串A,B要求最少的步骤把A串变成B串. 题目分析: 区间DP, 假如我们直接想把A变成B ...

  7. POJ-1088 滑雪 (记忆化搜索,dp)

    滑雪 Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 86318 Accepted: 32289 Description Mich ...

  8. HDU 4597 Play Game (记忆化搜索博弈DP)

    题意 给出2*n个数,分两列放置,每列n个,现在alice和bob两个人依次从任意一列的对头或队尾哪一个数,alice先拿,且两个人都想拿最多,问alice最后能拿到数字总和的最大值是多少. 思路 4 ...

  9. 【洛谷】3953:逛公园【反向最短路】【记忆化搜索(DP)统计方案】

    P3953 逛公园 题目描述 策策同学特别喜欢逛公园.公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条 ...

随机推荐

  1. win10前面板耳机没声音

    首先去装Relteck的驱动,windows64位的下载地址是: http://12244.wpc.azureedge.net/8012244/drivers/rtdrivers/pc/audio/0 ...

  2. jquery插件-表单验证插件-rules

    ruels方法 1说明:查看.新增.移除一个表单控件的验证规则 2使用: 表单控件.rules(); 参数: rules() 返回元素的验证规则 rules('add',rules) 增加验证规则 r ...

  3. NuGet在Push的时候提示“远程服务器返回错误:(403)已禁用”问题解决

    在使用NuGet把包push到nuget官网的时候,提示了如下信息: Failed to process request. 'The specified API key is invalid or d ...

  4. hdu 2857 求点关于线段的对称点

    本来很简单的一个题,但是有个大坑: 因为模板中Tline用到了直线的一般方程ax+by+c=0,所以有种很坑的情况需要特判: 斜率不存在啊喂 老子坑了一下午2333 #include <math ...

  5. 一、swoole安装

    说明:swoole扩展需要安装php环境,这里就不说了. 1.有了PHP环境后,即可安装swoole扩展. swoole扩展下载地址:https://github.com/swoole/swoole- ...

  6. Linux Process Virtual Memory

    目录 . 简介 . 进程虚拟地址空间 . 内存映射的原理 . 数据结构 . 对区域的操作 . 地址空间 . 内存映射 . 反向映射 .堆的管理 . 缺页异常的处理 . 用户空间缺页异常的校正 . 内核 ...

  7. ARPSpoofing教程(四) - 捕获并分析数据包

    本程序的主要目标是展示如何解析所捕获的数据包的协议首部. 这个程序可以称为UDPdump,打印一些网络上传输的UDP数据的信息. 1: #include"pcap.h" 2: ty ...

  8. POJ1236Network of Schools(强连通分量 + 缩点)

    题目链接Network of Schools 参考斌神博客 强连通分量缩点求入度为0的个数和出度为0的分量个数 题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后 ...

  9. nginx查看安装了哪些模块

    查看安装了哪些模块命令: [root@RG-PowerCache-X xcache]# nginx/sbin/nginx -Vnginx version: nginx/1.2.3built by gc ...

  10. easyUI draggable插件使用不当,导致拖动div内部文本框无法输入;设置echarts数据为空时就显示空白,不要动画和文字

    先上一个Demo <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://ww ...