1009 数字1的数量 数位dp
1级算法题就这样了,前途渺茫啊。。。
更新一下博客,我刚刚想套用数位dp的模板,发现用那个模板也是可以做到,而且比第二种方法简单很多
第一种方法:我现在用dp[pos][now]来表示第pos位数字为now时数字1的数量,如果用数位dp的话,现在我们有三种情况
第一种情况:now!=1,那我没什么好说的了,继续向下搜吧。
第二种情况:now==1&&limit :如果知道数位dp的套路的话,这个limit的意思相信是知道的,比如说一个数字1687,我们在程序走到pos==4,now==1时,这个时候limit==1,说明它后面的值有约束,只能从000到687,所以我们这个时候就要我们把千位的1全部加上就是688(1000也是一种)。
第三种情况:now==1&&limit==0:比如说2687,如果pos==4,now==1,这个时候limit==0,因为1_ _ _ 这里后面的三位数随便拿,反正不会超过2000就是了,这个时候把千位的1全部加起来就是pow(10,pos-1);
if(now==) //符合第二、三种情况的话
{
if(limit) //第二种情况
s+=add(pos-); //这个函数是把后面的数字加一遍(例如上面的0到687,返回值就是688)
else //第三种情况
s+=pow(,pos-);
}
然后完整代码:
#include<iostream>
#include<cstring>
#include<cstring>
#include<cmath>
using namespace std;
int dp[][],n,m,digit[];
int add(int a)
{
if(a<=) //比如1后面没有数字了,直接返回1
return ;
else
{
int count=;
for(int i=a;i>=;i--)
{
count+=digit[i]*pow(,i-);
}
return count+; //把全是0的情况也加一遍
}
}
int dfs(int pos,int now,int limit)
{
if(!pos)
return ;
int num=limit?digit[pos-]:;
int s=;
if(!limit&&dp[pos][now]!=-)
return dp[pos][now];
if(now==)
{
if(limit)
s+=add(pos-);
else
s+=pow(,pos-);
}
for(int i=;i<=num;i++)
{
s+=dfs(pos-,i,limit&&(i==num));
}
if(!limit&&dp[pos][now]==-)
dp[pos][now]=s;
return s;
}
int cal(int a)
{
int len=;
while(a)
{
digit[++len]=a%;
a/=;
}
return dfs(len+,,);//在这里我把它提前了一位,这个0改成-1什么的都可以,只要不是1就行了
}
int main()
{
cin>>n;
memset(dp,-,sizeof(dp));
cout<<cal(n)<<endl;
return ;
}
第二种方法:
给我们一个数字n,求从1到n中所有数字里1的数量;这里给的分类是数位dp,那我们先用dp[i][j]来表示从1到第i位为j的数字1的数量,
例如dp[3][2]就表示从1到299的数字1的数量,那怎么推呢?
假设现在是dp[3][1],也就是从1到199,这个数字更特殊一点点,那么我们可以把它表示为两个部分,1到99和100到199,
现在1到99很简单,就是dp[2][9]就是了,那100到199呢?其实我们可以把它的百位数分离,因为它的百位一定是1,
那么就一定有10^2个1,那就变成了10^2+(1到99之间数字1的数量)。
再来一个例子dp[3][2],现在是1到299了,我们可以把它变成(1到199)+(200到299),1到199就是dp[3][1],
而200到299,因为2不是1,那么200到299等价于1到99.
于是我们可以得出一个大致的结论:
dp[3][1]= dp[3][0] + dp[2][9] +(1==1)*pow(10,3-1);
dp[3][2]= dp[3][1] + dp[2][9] +(2==1)*pow(10,3-1);
dp[i][j]= dp[i][j-1] + dp[i-1][9] +(i==1)*pow(10,i-1);
但是j==0时,j-1等于0,那么我们可以改进一下:
if(j==0)
dp[i][j]=dp[i-1][9]; dp[3][0]等价于dp[2][9]
else
dp[i][j]=dp[i][j-1]+dp[i-1][9]+(i==1)*pow(10,i-1);
终于把这个东西推完了,但是我们怎么表示一个任意的n呢?
比如说n=435,好,现在我们要求1到435之间的1的数量,435可以分为,(1到399)+(400到435)。
而400到435又可以把4分离,如果4==1(额,我是说如果这个百位上的数字为1),好吧这不可能,那就是(1到399)+(1到35)了,然后35又持续这个过程。。。,但是当n=135时,就是(1到99)+(1到35)+(35+1),最后这个(35+1)是因为百位为1,那么就要加上百位上的1,即(100到135)百位上的1有(35+1)个。直接看代码,我表达不行。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
ll dp[][],ss[],n,m;
int main()
{
cin>>n;
memset(dp,,sizeof(dp));
for(int i=;i<=;i++)
dp[][i]=;
for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
if(j==)
dp[i][j]=dp[i-][];
else
dp[i][j]=dp[i][j-]+dp[i-][]+pow((j==)*,i-);//先推出
}
}
int digit[];
int len=;
ll m=n;
while(n)
{
digit[++len]=n%;//把每一位上的数字存下来
n/=;
}
for(int i=len;i>=;i--)
{
ll s=pow(,i-);//例如1435,把435,35 ,5都存下来
ss[i]=m%s;
}
ll ans=;
for(int i=len;i>=;i--)
{
if(i>)
ans+=dp[i][digit[i]-];
else
ans+=dp[i][digit[i]];
if(digit[i]==&&i>)//判断这一位是不是1,
{
ans+=ss[i]+;
}
}
cout<<ans<<endl;
return ;
}
1009 数字1的数量 数位dp的更多相关文章
- 51nod 1009 数字1的数量 数位dp
1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1 ...
- 51nod 1009 - 数字1的数量 - [数位DP][模板的应用以及解释]
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009 基准时间限制:1 秒 空间限制:131072 KB 给 ...
- 51nod 1042 数字0-9的数量 数位dp
1042 数字0-9的数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注 给出一段区间a-b,统计这个区间内0-9出现的次数. 比如 10-1 ...
- 51Nod 1009 数字1的个数 | 数位DP
题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9 ...
- 1001 数组中和等于K的数对 1002 数塔取数问题 1003 阶乘后面0的数量 1004 n^n的末位数字 1009 数字1的数量
1001 数组中和等于K的数对 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数,找出数组A中所有和等于K ...
- 51nod 1009 数字1的数量
1009 数字1的数量 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5 ...
- [51 nod]1009 数字1的数量
1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如: ...
- 51Nod 1009:1009 数字1的数量 (思维)
1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 收藏 关注 给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的 ...
- 51nod 1009 数字1的数量(数位dp模板)
给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1. 数位dp的模板题 ...
随机推荐
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
转自:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的 ...
- 白鹭引擎 - 本地坐标和舞台坐标的转化 ( globalToLocal, localToGlobal )
class Main extends egret.DisplayObjectContainer { /** * Main 类构造器, 初始化的时候自动执行, ( 子类的构造函数必须调用父类的构造函数 ...
- 【ASP.NET 进阶】PDF文件在线预览(类似百度文库)
工作需要完成文档的在线预览,现在完成了第一步PDF文件的预览,步骤是通过PDF转换工具pdf2swf.exe把PDF文件转换为SWF文件,然后通过FlexPaper就可以预览了.效果如下(GIF图片太 ...
- python内置函数使用
print(abs(1)) #绝对值,正数就是自己 ",''])) #计算可迭代对象中是否为真,其中一个为假,就显示为假 print(all('')) # If the iterable i ...
- react-native android 权限问题
初入reactNative 的一个坑 我是用的真机测试,没用安卓模拟器 第一次在安卓上打开应用,提示权限问题: Overlay permissions needs to be granted in o ...
- JConsole 配置
Tomcat 1:修改catalina.sh文件如下 JAVA_OPTS="-Djava.rmi.server.hostname=XXX.XXX.XXX.XXX -Dcom.sun.mana ...
- 回溯法 leetcode题解 Combination Sum 递归法
题目大意:给出一个数组,用这些数组里的元素去凑一个target.元素可以重复取用. 感觉对这种题目还是生疏的.脑子里有想法,但是不知道怎么表达出来. 先记录下自己的递归法.应该还可以用循环实现. 回溯 ...
- P12, cer, provisioning profile
p12,本地私钥(实际上证书和私钥可以一起导出成p12,这里我们仅指私钥) cer,证书,即苹果签名后的公钥 provisioning profile描述文件 一个证书对应一个私钥,也就是本地的p12 ...
- C++复习:纯虚函数和抽象类
纯虚函数和抽象类 1基本概念 2抽象类案例 3抽象类在多继承中的应用 C++中没有Java中的接口概念,抽象类可以模拟Java中的接口类.(接口和协议) 3.1有关多继承的说明 工程上的多继承 被 ...
- LeetCode OJ 19. Remove Nth Node From End of List
Given a linked list, remove the nth node from the end of list and return its head. For example, Give ...