码迷,mamicode.com
首页 > 其他好文 > 详细

hdu3555 Bomb (记忆化搜索 数位DP)

时间:2014-08-13 22:07:17      阅读:283      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   color   java   os   io   

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这么大,不可能枚举。不过我们先看看按位从高到低的枚举深搜:

 1 ll dfs(int w, int state, bool limit) {
 2     if(w<0) return state==2;
 3 
 4     int maxi=limit ? a[w] : 9;
 5     ll re=0;
 6     for(int i=0; i<=maxi; i++) {
 7         int s=state;
 8         if(state==1) {
 9             if(i==9) s=2;
10             else if(i!=4)s=0;
11         } else if(state==0 && i==4) s=1;
12         re+=dfs(w-1, s, limit && i==a[w]);
13     }
14 
15     return re;
16 }

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[][]是和上面记忆化搜索的一样的:

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

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

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

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

 

记忆化搜索全代码:

bubuko.com,布布扣
 1 //#pragma comment(linker, "/STACK:102400000,102400000")
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<iostream>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<cmath>
 8 using namespace std;
 9 #define ll long long
10 
11 ll f[20][3];///f[i][j],i位数,j=0是没49的,1是9开头的,2是有49的
12 int a[20],an;
13 
14 ll dfs(int w, int state, bool limit) {
15     if(w<0) return state==2;
16     if(!limit && f[w][state]!=-1) return f[w][state];
17 
18     int maxi=limit ? a[w] : 9;
19     ll re=0;
20     for(int i=0; i<=maxi; i++) {
21         int s=state;
22         if(state==1) {
23             if(i==9) s=2;
24             else if(i!=4)s=0;
25         } else if(state==0 && i==4) s=1;
26         re+=dfs(w-1, s, limit && i==a[w]);
27     }
28 
29     if(!limit) f[w][state]=re;
30     return re;
31 }
32 
33 ll farm(ll x) {
34     an=0;
35     while(x) {
36         a[an++]=x%10;
37         x/=10;
38     }
39     return dfs(an-1,0,1);
40 }
41 
42 int main() {
43     int T;
44     ll n;
45     memset(f,-1,sizeof(f));
46     scanf("%d",&T);
47     while(T--) {
48         scanf("%I64d",&n);
49         printf("%I64d\n",farm(n));
50     }
51     return 0;
52 }
View Code


数位DP全代码:

bubuko.com,布布扣
 1 //#pragma comment(linker, "/STACK:102400000,102400000")
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<iostream>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<cmath>
 8 using namespace std;
 9 #define ll long long
10 
11 ll f[20][3];///f[i][j],i位数,j=0是没49的,1是9开头的,2是有49的
12 int a[20],an;
13 
14 ll farm(ll x) {
15     an=0;
16     while(x) {
17         a[an++]=x%10;
18         x/=10;
19     }
20     a[an]=0;
21     ll ans=0;
22     bool flag=false;
23     for(int i=an-1;i>=0;i--){
24         ans+=(ll)f[i][2]*a[i];///后面有49,这一位取0~a[i]-1(取a[i]的情况在之后的循环中计算)
25         if(flag) ans+=(ll)f[i][0]*a[i];///前面全固定取a[i],有49了,这位0~a[i]-1,后面取没49的(后面有49的情况已经在上一句算过了)
26         else if(!flag && a[i]>4)///前面全固定取a[i],没49,这一位能取4,后面开头为9的情况数f[i][1],则就是组成49的情况数
27             ans+=f[i][1];
28         if(a[i]==9 && a[i+1]==4) flag=true;///这一位取a[i],上一位取a[i+1]时成49,标flag,日后计算
29     }
30     return ans;
31 }
32 
33 void init(){
34     memset(f,0,sizeof(f));
35     f[0][0]=1;
36     for(int i=1;i<20;i++){
37         f[i][0] = (ll)f[i-1][0]*10 - f[i-1][1]; ///没49的 = 之前没49的这一位随便 - 之前开头是9的这一位是4
38         f[i][1] = f[i-1][0];                    ///开头是9的 = 之前没49的这1位是9
39         f[i][2] = (ll)f[i-1][2]*10 + f[i-1][1]; ///有49的 = 之前有49的这一位随便 + 之前开头是9的这一位是4
40     }
41 }
42 
43 int main() {
44     int T;
45     ll n;
46     init();
47     scanf("%d",&T);
48     while(T--) {
49         scanf("%I64d",&n);
50         printf("%I64d\n",farm(n+1));///farm(x)是算小于x的
51     }
52     return 0;
53 }
View Code

 

hdu3555 Bomb (记忆化搜索 数位DP),布布扣,bubuko.com

hdu3555 Bomb (记忆化搜索 数位DP)

标签:des   style   blog   http   color   java   os   io   

原文地址:http://www.cnblogs.com/yuiffy/p/3911116.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!