题目链接: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[];
ll dp[][];
int N; ll dfs(int pos,int cnt,bool limit)
{
if(pos==) return cnt; //已经精确到某一个数,那么直接返回这个数有几个“1”.
if(!limit && ~dp[pos][cnt]) return dp[pos][cnt]; //如果曾今计算过dfs(pos,cnt,0),直接返回dp[pos][cnt] int up=limit?dig[pos]:; //确定当前第pos位的枚举上界
int ans=; //定义ans变量,记录累加结果,在函数最后return ans
for(int i=;i<=up;i++) //暴力枚举当前第pos位的数
{
if(i==) ans+=dfs(pos-,cnt+,limit && i==up); //如果当前位为1
else ans+=dfs(pos-,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=;
while(x) //将上界N记录到dig数组中
{
dig[++pos]=x%;
x/=;
}
return dfs(pos,,);
} int main()
{
while(scanf("%d",&N)!=EOF)
{
memset(dp,-,sizeof(dp));
printf("%lld\n",solve(N));
}
}

51nod 1009 - 数字1的数量 - [数位DP][模板的应用以及解释]的更多相关文章

  1. 51nod 1009 数字1的数量 数位dp

    1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB   给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数.   例如:n = 12,包含了5个1 ...

  2. 1009 数字1的数量 数位dp

    1级算法题就这样了,前途渺茫啊... 更新一下博客,我刚刚想套用数位dp的模板,发现用那个模板也是可以做到,而且比第二种方法简单很多 第一种方法:我现在用dp[pos][now]来表示第pos位数字为 ...

  3. 51nod 1042 数字0-9的数量 数位dp

    1042 数字0-9的数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题  收藏  关注 给出一段区间a-b,统计这个区间内0-9出现的次数.   比如 10-1 ...

  4. 51Nod 1009 数字1的个数 | 数位DP

    题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9 ...

  5. 51nod 1009 数字1的数量

    1009 数字1的数量   给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数.   例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5 ...

  6. 51nod 1009 数字1的数量(数位dp模板)

    给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1.   数位dp的模板题   ...

  7. 51Nod 1009:1009 数字1的数量 (思维)

    1009 数字1的数量  基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题  收藏  关注 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的 ...

  8. 1001 数组中和等于K的数对 1002 数塔取数问题 1003 阶乘后面0的数量 1004 n^n的末位数字 1009 数字1的数量

    1001 数组中和等于K的数对 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数,找出数组A中所有和等于K ...

  9. 51nod 1042数字0-9的数量

    1042 数字0-9的数量  基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题  收藏  关注 给出一段区间a-b,统计这个区间内0-9出现的次数. 比如 10-19 ...

随机推荐

  1. android 不能在子线程中更新ui的讨论和分析

    问题描写叙述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是仅仅能在主线程中更改 ui.子线程要 ...

  2. 修改Tomcat的默认访问目录

    放在外网的应用,用户多是直接输入域名访问,而Tomcat的默认目录是ROOT,所以我们需要更改其默认目录. 更改Tomcat的默认目录很简单,只需要修改server.xml就可以了. 具体是是在< ...

  3. ios开发之-- tableview/collectionview获取当前点击的cell

    方法如下: 一般collectionView 或者 tableview都有自带的点击函数,如下: , collectionView -(void)collectionView:(UICollectio ...

  4. Linux应急响应(三):挖矿病毒

    0x00 前言 ​ 随着虚拟货币的疯狂炒作,利用挖矿脚本来实现流量变现,使得挖矿病毒成为不法分子利用最为频繁的攻击方式.新的挖矿攻击展现出了类似蠕虫的行为,并结合了高级攻击技术,以增加对目标服务器感染 ...

  5. WAF Bypass FUZZ小脚本

    分享两个小脚本,用来WAF Bypass简单FUZZ的 第一个:先生成一个字典,带入搭建的环境进行FUZZ,针对某些软WAF挺好用的,可FUZZ出不少姿势出来,记得先把CC攻击加入白名单才行哦... ...

  6. mac 常用的终端命令

    OSX 的文件系统 OSX 采用的Unix文件系统,所有文件都挂在跟目录 / 下面,所以不在要有Windows 下的盘符概念. 你在桌面上看到的硬盘都挂在 /Volumes 下. 比如接上个叫做 US ...

  7. flask路由中增加正则表达式

    #coding=utf-8 from flask import Flask from werkzeug.routing import BaseConverter class RegexConverte ...

  8. cp自动创建层级结构的例子

    一个拷贝命令的技巧,不仅拷贝文件,而且拷贝目录结构.记录下来. *拷贝的时候,自动创建参数中源文件的路径:#cp --parents parentdir1/parentdir2/sourcefile ...

  9. thinkphp3.2 判断星期几

    后台 $w=date('w'); $week=array( "=>"星期日", "=>"星期一", "=>&qu ...

  10. 如何在浏览器中简单模拟微信浏览器(仅限于通过User Agent进行判断的页面)

    模拟微信浏览器: .打开360极速 .F12开发者工具 .开发者模式左上方有一个手机样子的图标 点击进入 设备模式‘ .将UA选项中的字符串替换成: Mozilla/ 备注: 替换的字符串是微信浏览器 ...