数位DP-HDU-3555-Bomb

来源:互联网 发布:少男的喜欢lofter乐乎 编辑:程序博客网 时间:2024/05/22 22:23

Bomb

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

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
2010 ACM-ICPC Multi-University Training Contest(12)——Host by WHU

题意是给一个数N,要求1到N中,数位中出现连续的49的数有多少个,比如49,149,249,499之类的。

初次接触数位DP,真的是头都大了,一开始自己瞎想,弄个dp[size][start]来存第size位是start的情况下满足题意的数的个数,最后由于不好处理,没法输出。
后来只好又去查了查,看了些博客,总算把这道题看懂,大致还是分情况讨论了,其实和DFS差不多的感觉,不过看见有个人用DFA做这道题,有点不明觉厉。
先将N按数位存在num[]数组中。
用dp[i][0]存储0~99..9(i个9)中不满足题意的数的个数。
用dp[i][1]存储同上的范围中不满足题意的以9作为第i位数的个数。
用dp[i][2]存储同上的范围中满足题意的数的个数。
当求dp[i][0]时,由于第i位可以取0~9,同时后i-1位必须不满足,所以先加上十个dp[i-1][0],而后,由于第i位取4,第i-1位取9的时候,不满足,所以还要减去dp[i-1][1]。
那么dp[i][0]=dp[i-1][0]*10-dp[i-1][1]。
当求dp[i][1]时,第i位可以取9,那么只要在后i-1位的不满足题意的数前加一个9即可。
即dp[i][1]=dp[i-1][0]。
当求dp[i][2]时,第i位可以取0~9,那么如果后i-1位已经满足条件,随便取第i位都可以,那么先加上10个dp[i-1][2],同时如果第i位取4,第i-1位取9,那么将新增dp[i-1][1]个满足题意的数,加上。
得到dp[i][2]=10*dp[i-1][2]+dp[i-1][1]。
至此可以预处理出题中要求范围的所有dp[i][0~2]。

那么对于一个数N,又要怎样处理呢。
首先我们已经把N存在了num[]数组中,并且能够得到其长度size,令最终输出为sum,初始值为0。
处理方式是从最高位向最低位累加,加到第i位时,前size-i位都取了最大的值作为当前的参考。
如果当前已经处理到第i位(i>=1),那么如果后i-1位是符合的,第i位随便取也能符合(当然不能超过num[i]),那么先加上dp[i-1][2] x num[i]。同时,如果在第i位之前就已经在num[]中出现过连续49,那么这后i位随便取都能符合题意,由于已经加了num[i]个dp[i-1][2],所以还要加上num[i]个dp[i-1][0](由数组定义可知dp[i-1][0]+dp[i-1][2]是一定等于99..9(i-1个)的)。如果第i位之前没有出现过49,那么如果num[i]>4,当第i位取4,第i-1位取9时,又能获得dp[i-1][1]个符合题意的数,所以再加上dp[i-1][1](当然,如果已经出现了49,就不要重复加了)。
一直处理到第1位,此时如果前面出现过49,就加一(因为是没有考虑其本身的)。
输出sum即可。

在做了HDU2089之后,我又用我上述的dp[size][start]的方法做了一次这道题,时间降到15ms。
附上2089方法详解:http://blog.csdn.net/roy_yuan/article/details/49500063


原方法

////  main.cpp//  数位DP-D-Bomb////  Created by 袁子涵 on 15/10/29.//  Copyright © 2015年 袁子涵. All rights reserved.////  62ms    1604KB#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <math.h>#include <stdlib.h>using namespace std;int T;long long int dp[20][3],N;int num[20];void handle(){    dp[0][0]=1;    for (int i=1; i<=18; i++) {        dp[i][0]=dp[i-1][0]*10-dp[i-1][1];        dp[i][1]=dp[i-1][0];        dp[i][2]=dp[i-1][2]*10+dp[i-1][1];    }}long long int DP(){    int size=0;    long long int temp=N;    memset(num, 0, sizeof(num));    while (temp) {        num[++size]=temp%10;        temp/=10;    }    bool flag=0;    for (int i=size; i>0; i--) {        temp+=dp[i-1][2]*num[i];        if (flag)            temp+=dp[i-1][0]*num[i];        else if (num[i]>4)            temp+=dp[i-1][1];        if (num[i+1]==4 && num[i]==9)            flag=1;    }    if (flag)        temp++;    return temp;}int main(int argc, const char * argv[]) {    cin >> T;    memset(dp, 0, sizeof(dp));    handle();    while (T--) {        cin >> N;        long long int out=0;        out=DP();        cout << out << endl;    }    return 0;}

最新

////  main.cpp//  数位DP-D-Bomb-New////  Created by 袁子涵 on 15/10/30.//  Copyright © 2015年 袁子涵. All rights reserved.////  15ms    1596KB#include <stdio.h>#include <string.h>#include <stdlib.h>#include <algorithm>#include <math.h>#include <iostream>using namespace std;int T;long long int N,dp[20][10];void handle(){    for (int i=0; i<10; i++)        dp[1][i]=0;    for (int i=2; i<=18; i++) {        for (int j=0; j<=9; j++) {            dp[i][j]=0;            if (j==4) {                for (int k=0; k<=9; k++) {                    if (k==9)                        dp[i][j]+=(long long int )pow(10, i-2);                    else                        dp[i][j]+=dp[i-1][k];                }                continue;            }            else                for (int k=0; k<=9; k++) {                    dp[i][j]+=dp[i-1][k];                }        }    }}long long int DP(long long int N){    char num[20];    memset(num, 0, sizeof(num));    int size=0;    long long int sum=0;    while (N) {        num[++size]=N%10;        N/=10;    }    bool flag=0;    for (int i=size; i>=1; i--) {        if (flag)        {            sum+=num[i]*pow(10, i-1);            continue;        }        for (int j=num[i]-1; j>=0; j--)            sum+=dp[i][j];        if (num[i]==9 && num[i+1]==4)            flag=1;    }    return sum;}int main(){    memset(dp, 0, sizeof(dp));    memset(dp, 0, sizeof(dp));    handle();    cin >> T;    while (T--) {        scanf("%lld",&N);        N=DP(N+1);        printf("%lld\n",N);    }    return 0;}
0 0
原创粉丝点击