HDU 3555 Bomb (数位DP)

题意:给定一个数,让你求从1到这个数的所有数字中含49的数字数量。

析:看到这一个题,首先想到的就是暴力,可是,我一看数的大小就放弃了,2^63-1。。。

还是问学长吧,学长说数位DP,听他说了好久,才明白,好复杂啊,在这里分享一下。

这是我参考的博客网址:http://www.cnblogs.com/liuxueyang/archive/2013/04/14/3020032.html

d[i][0]:表示前 i 位中不含49的数量;

d[i][1]:表示前 i 位中不含49,但是最高位是9(也就是说第 i 位是9),的数量;

d[i][2]:表示前 i 位中含49的数量。

这前 i 位是从低位到高位的。那应该怎么转移呢?

d[i][0] = d[i-1][0] * 10 - d[i-1][1];  表示长度为 i 的不含有49的数字的个数等于长度为 i - 1 的不含有49的数字的个数*10。

为什么呢?是这样的,d[i-1][0] * 10,这个包含了一种它不应该有的情况,那就是第 i 位是4,而第 i-1 位是9的,所以要减去。

dp[i][1] = dp[i-1][0];表示长度为 i 的并且不含有49同时最高位是9的数字的个数等于,长度为 i - 1 的不含有49的数字的个数,

因为只要在它的高一位加上一个9就可以了。所以是这样。

dp[i][2] = dp[i-1][2] * 10+ dp[i-1][1]; 表示长度为 i 的含有49的数字的个数等于,长度为 i - 1 的数字的个数*10,

再加上长度为 i - 1 的并且不含有49同时最高位是9的数字的个数,为什么呢,这是因为还得加上第 i-1 位是9,而第 i 位是4的情况。

这是预处理一下的。下面是具体计算时的DP。

1.首先加上 i-1位含有49的情况。也就是 ans += d[i-1][2] * a[i];

2.再看看是不是以前出现过49,如果出现了,就再加上现在没有49的情况,即 if(出现过)  ans += d[i-1][0] * a[i];

3.如果没有出现过,就要判断这一位是不是大于4,如果大于4,就要再追加上长度为 i - 1 的不含有49但是最高位是9的数字的个数,

因为这个时候可以再这一位填4,肯定能和前组成一个49,因为它是大于4的。即 if(第 i 位大于4 && 没有出现49)  ans += d[i-1][1];

4.那就是判断一下有没有49出现,如果有就标记,没有就算了,即  if(出现49了) ok = true;

最后就是在输入的时候,要计算n+1,而不是n,为什么呢?我的个人理解是这样的,如果不加1的话,那么最后一步,只标记了出现49了,

但是并没有把它计算上,所以是n+1。还要注意,这个输入数转化为字符时要把从下标1开始。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <map>
#include <cctype>
#include <cmath>

using namespace std;
typedef long long LL;
const int maxn = 30;
LL d[20][3], a[25];//用 long long

void init(){//先处理一下
    memset(d, 0, sizeof(d));
    d[0][0] = 1;
    for(int i = 1; i <= 20; ++i){
        d[i][0] = d[i-1][0] * 10L - d[i-1][1];
        d[i][1] = d[i-1][0];
        d[i][2] = d[i-1][2] * 10L + d[i-1][1];
    }
}

LL solve(LL n){
    int len = 0;
    while(n){//转化成字符串,当然也可以用sprintf,不过我不怎么用
        a[++len] = n % 10L;
        n /= 10L;
    }
    a[len+1] = 0;//清空一下,以免出现巧合
    LL ans = 0;
    bool ok = false;
    for(int i = len; i > 0; --i){
        ans += d[i-1][2] * a[i];
        if(ok)  ans += d[i-1][0] * a[i];//出现过
        if(a[i] > 4 && !ok)  ans += d[i-1][1];//大于4,并且没有出现过
        if(a[i] == 9 && a[i+1] == 4)  ok = true;//标记出现过49
    }
    return ans;
}

int main(){
    init();
    int T;  cin >> T;
    LL n;
    while(T--){
        scanf("%lld", &n);//注意输入输出
        printf("%lld
", solve(n+1));//注意是n+1
    }
    return 0;
}
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std;

typedef long long LL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 0x3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline int Min(int a, int b){ return a < b ? a : b; }
inline int Max(int a, int b){ return a > b ? a : b; }
inline LL Min(LL a, LL b){ return a < b ? a : b; }
inline LL Max(LL a, LL b){ return a > b ? a : b; }
inline bool is_in(int r, int c){
    return r >= 0 && r < n && c >= 0 && c < m;
}
LL dp[25][2], a[25];

LL dfs(int pos, bool is, bool ok){
    if(!pos)  return 1;
    LL &ans = dp[pos][is];
    if(!ok && ans >= 0) return ans;

    LL res = 0, n = ok ? a[pos] : 9;
    for(int i = 0; i <= n; ++i){
        if(is && 9 == i)  continue;
        res += dfs(pos-1, i == 4, ok && i == n);
    }
    if(!ok)  ans = res;
    return res;
}

LL solve(LL n){
    int len = 0;
    while(n){
        a[++len] = n % 10;
        n /= 10;
    }
    return dfs(len, false, true);
}

int main(){
    memset(dp, -1, sizeof dp);
    int T;  cin >> T;
    while(T--){
        LL x;
        scanf("%lld", &x);
        printf("%lld
", x-solve(x)+1);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dwtfukgv/p/5559103.html