zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Java知识点梳理——集合
    Java知识点梳理——装箱和拆箱
    Shiro RememberMe 1.2.4 反序列化漏洞详细复现
    哈希长度拓展攻击之De1CTF
    Discuz! ML RCE漏洞 getshell 复现
    Redis基于主从复制的RCE 4.x/5.x 复现
    WebLogic反序列化漏洞(CVE-2019-2725补丁绕过)
    【实战2】记一次获取远程桌面历程
    远程桌面远程代码执行漏洞(CVE-2019-0708)Poc
    【实战1】记一次提至administrator权限历程
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/5559103.html
Copyright © 2011-2022 走看看