zoukankan      html  css  js  c++  java
  • 51nod 1009

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009

    基准时间限制:1 秒 空间限制:131072 KB

    给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数。
     
    例如:n = 12,包含了5个1。1,10,12共包含3个1,11包含2个1,总共5个1。
    Input
    输入N(1 <= N <= 10^9)
    Output
    输出包含1的个数
    Input示例
    12
    Output示例
    5

    题解:

    这道题跟前面的HDU2089HDU3555这两道题有一点不同,dp数组的设定不是很一样。

    本题解的前提是,默认为阅读过“数位dp总结 之 从入门到模板 http://blog.csdn.net/wust_zzwh/article/details/52100392”;

    首先我们可以清楚地定义好,输入一个上界,那么它的位数就是“最大位数”,暂且定义为len;

    然后我们的pos,定义域是在[0,len],它其实不是准确指定某一个长度的数;

    而是说,它现在这个dp[pos][…]值,我们只管到范围从第1位到第pos位,再宽的话,抱歉我管不了。

    首先,我们说清楚“位”:

      

    接下来我们要设计dfs函数;

    dfs(pos,cnt,limit)代表:

      已知第pos+1位到第len位上已有cnt个“1”(这是状态state);

      已知我现在要进行枚举的第pos位有没有上界限制(有上界限制的话上界up = dig[pos],没有的话上界up = 9);

      然后这个函数就能给你返回:第1位到第pos位上枚举所有可能出现的数字(每一位都可任意填0~9),连接上前面第pos+1位到第len位上已经确定下来的数字,共出现了多少个“1”。

    例如:

      N = 1200,len = 4,pos= 2,limit = 0,已确定第3~4位是“01”(即cnt=1,出现了一个“1”),

      第1位到第2位上就可以从00枚举到99,那么dfs(2,1,0)的返回值就是0100到0199出现了多少个“1”;

    这样设计的一个原因是,我们必须要求我们的dfs(pos,cnt,limit)能够在传入参数后,能迭代返回我们需要的答案。

    换句话说,样例输入N = 1200,那么配合一个dig数组(dig[1]=0,dig[2]=0,dig[3]=2,dig[4]=1),我们的dfs(4,0,1)函数就能给你正确答案。

    接下来是关于dp数组的定义:

    dp数组可以说是dfs函数的记忆化,可以说dp[pos][cnt]的值就等于dfs(pos,cnt,0);

    只要你算过一次dfs(pos,cnt,0),就用dp[pos][cnt]给你记录下来,在没有上界限制的情况下,可以直接使用dp[pos][cnt],而不用再去dfs(pos,cnt,0),节省大量时间。

    例如:

      继续用前面的例子,N = 1200,len = 4,pos= 2,limit = 0,

      确定第3~4位是“01”(即cnt=1,出现了一个“1”),或者第3~4位是“10”(依然cnt=1,出现了一个“1”),

      第1位到第2位上从00枚举到99,那么“0100到0199”和“1000到1099”出现的“1”的个数是一样的,

      那么我们记录下“0100到0199”的第一次dfs(2,1,0)的返回值,存储在dp[2][1],那么之后要去算“1000到1099”的时候直接返回dp[2][1]即可;

    然后设计dfs()函数的转移:

      dfs(pos,cnt,limit) 等于:暴力枚举第pos位上的数:i = 0 ~ ( limit ? dig[pos] : 9 ),累加起所有dfs( pos - 1, cnt + ( i==1?1:0) , limit && i == dig[pos] ).

    最后!……配上数位DP的板子,就很好写啦。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int dig[11];
    ll dp[11][11];
    int N;
    
    ll dfs(int pos,int cnt,bool limit)
    {
        if(pos==0) return cnt; //已经精确到某一个数,那么直接返回这个数有几个“1”.
        if(!limit && ~dp[pos][cnt]) return dp[pos][cnt]; //如果曾今计算过dfs(pos,cnt,0),直接返回dp[pos][cnt]
    
        int up=limit?dig[pos]:9; //确定当前第pos位的枚举上界
        int ans=0; //定义ans变量,记录累加结果,在函数最后return ans
        for(int i=0;i<=up;i++) //暴力枚举当前第pos位的数
        {
            if(i==1) ans+=dfs(pos-1,cnt+1,limit && i==up); //如果当前位为1
            else ans+=dfs(pos-1,cnt,limit && i==up); //如果当前位不为1
        }
    
        if(!limit) dp[pos][cnt]=ans; //将dfs(pos,cnt,0)记录到dp[pos][cnt]
        return ans;
    }
    ll solve(int x)
    {
        int pos=0;
        while(x) //将上界N记录到dig数组中
        {
            dig[++pos]=x%10;
            x/=10;
        }
        return dfs(pos,0,1);
    }
    
    int main()
    {
        while(scanf("%d",&N)!=EOF)
        {
            memset(dp,-1,sizeof(dp));
            printf("%lld
    ",solve(N));
        }
    }
  • 相关阅读:
    第一课:神经网络与机器学习
    自然语言基础之分词、标注、命名实体识别
    工程能力-语言-框架
    二叉树
    spark入门
    微平台推荐系统介绍(基于java)
    简历项目的梳理和面试准备
    统计学习方法李航学习笔记
    NopCommerce fluent validation使用
    NopCommerce支持多语言
  • 原文地址:https://www.cnblogs.com/dilthey/p/8503376.html
Copyright © 2011-2022 走看看