美文网首页动态规划动态规划
求含有6的数---数位dp/dfs

求含有6的数---数位dp/dfs

作者: ffffffffffffly | 来源:发表于2019-01-29 00:56 被阅读0次

2019牛客网寒训营3#G

经过了选号和漫长的等待,处女座终于拿到了给小姐姐定制的手环,小姐姐看到以后直呼666!

处女座其实也挺喜欢6这个数字的,实际上他做手环的时候选取的k=6。所以他对于包含数码6的数字极其敏感。每次看到像4567这样的数字的时候他的心就像触电了一样,想起了小姐姐。

题目描述

现在你要给处女座展示一系列数字,你想知道他的内心会激动多少次。对于同一个数字,他最多只会激动一次,即如果这个数是66666,他还是只会激动一次。

输入描述:

一行包括两个数字l,r,表示你给处女座展示的数字范围为[l,r]。

输出描述:

一行一个整数,表示处女座内心激动的次数。

示例1

输入
10 20

输出
1

备注:

0≤l≤r≤10^18

第一种方法:数位dp,用【总数 - 不含6的数 = 所求】,那问题就变成求不含6的数,用二维数组dp[i][j]表示有i位数,首位是j的不含6的数,但有一个前提是:枚举第i位,默认第i+1位上是确定的。则: dp[i] [j] += \left\{\begin{matrix} 0 & ,j=6 \\ \sum_{k=0,k != 6}^{9} dp[i-1][k]& , j != 6 \end{matrix}\right.

代码

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20][10];  //i位数,以j为首位的不含6的个数

void init()
{
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for(int i=1; i<20; ++i)
        for(int j=0; j<10; ++j)
            for(int k=0; k<10; ++k)  //k也要从0开始,与j一样,表示首位
                if(j != 6)
                    dp[i][j] += dp[i-1][k];
}

ll solve(ll n)
{
    int digit[20]; //n每一位上的数
    int k = 0;
    ll temp = n;
    ll ans=0;
    while (temp){
        digit[++k] = temp % 10;
        //cout << digit[k] << endl;
        temp /= 10;
    }
    digit[k+1] = 0;   //
    for(int i = k; i > 0; --i){  //从左到右,从高位到低位
        for(int j = 0; j < digit[i]; ++j){
            if(j != 6)
                ans += dp[i][j];
        }
        if(digit[i] == 6) break; //若等于6,则直接退出,后面的数已经计算过了
    }
    return n - ans;  //总数减去不含有6的数
}

int main()
{
    init();   //别漏了初始化
    ll l, r;
    while(cin >> l >> r){
        cout << solve(r+1) - solve(l) << endl;
    }
    return 0;
}

第二种方法:深搜dfs,从左到右一位位的搜索,注意有一个limit变量用来判断枚举的范围,判断上一位是否已经取到最大,如果不是,则下一位可以取到9。详细可见注释:

AC代码如下

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20][10];
int digit[10];

ll dfs(int pos, int num, bool limit)
{//limit为真表示上一位取到最大值,eg.213,若首位取2,则为true,若为0/1,则为false
    if(!pos) return 1;
    if(!limit && dp[pos][num] != -1) return dp[pos][num];  //上一位没有取最大值,且当前这个数已经算出来过了
    ll ans = 0;
    int now = limit ? digit[pos] : 9;  //若上一位没有取到最大值,则当前位可以取到9.
    for(int i=0; i<=now; ++i){
        if(i == 6) continue;
        ans += dfs(pos-1, num, limit && i==now); //深搜,一直减少位数,若上一位取得最大,当前位也计算完了,则当前表示为取满
    }
    return limit ? ans : (dp[pos][num] = ans); //上一位取得最大,则直接返回ans
}

ll solve(ll n)
{
    memset(digit, 0, sizeof(digit));
    int k = 0;
    while(n){
        digit[++k] = n%10;
        n /= 10;
    }
    return dfs(k, 0, true);
}

int main()
{
    ll l, r;
    while(cin >> l >> r){
        memset(dp, -1, sizeof(dp));
        cout << r-solve(r) - (l-solve(l-1)-1) << endl;
    }
    return 0;
}

相关文章

  • 求含有6的数---数位dp/dfs

    2019牛客网寒训营3#G 经过了选号和漫长的等待,处女座终于拿到了给小姐姐定制的手环,小姐姐看到以后直呼666!...

  • 数位dp

    数位dp的入门题HDU - 2089 不要62题意:求区间[n,m]之间,不含有4和(连续的)62的数字个数。分析...

  • 数位DP,统计1-N中含有“49”的总数

    题目:参考数位DP,统计1-N中含有“49”的总数 另一篇参考文章HDU 3555 Bomb(1-n含有“49”的...

  • 数位dp

    数位dp是解决一类选择有约束的数字的个数的问题的解法,就是数一个区间有多少个满足题目条件的数字的个数,通常暴...

  • 数位DP

    1. 计数问题 原题链接[https://www.acwing.com/problem/content/340/]...

  • DP训练——数位DP

    数位DP BZOJ1026题意求到间,不含前导零且相邻两个数字之差至少为的正整数的个数。题解状态定义:表示当前处理...

  • Leetcode 【39、40、77】

    问题描述:【DFS、DP】39. Combination Sum 解题思路: 这道题和 Leetcode 【DP】...

  • 【进阶】数位DP详解

    今天,我向大家介绍一种特殊的DP类型——数位DP。数位DP这类题目一般不会出现在提高组及以下的比赛中(今后出现了当...

  • N个骰子的点数

    先求初始状态 dp[1,1] => dp[1,6],dp[x,y]中x代表骰子数量,y代表点数和。dp[x,y]=...

  • 动态规划-数位Dp

    记录今天在Acwing学习的几道数位Dp题目,整理了思路,方便以后的复习: 1.度的数量 题目描述 求给定区间 [...

网友评论

    本文标题:求含有6的数---数位dp/dfs

    本文链接:https://www.haomeiwen.com/subject/wvqajqtx.html