1049 Counting Ones (30 分)
题目传送门:1049 Counting Ones (30 分)
一看到这道题就知道考的是数位DP,倔强的我不想找模板~~,自己写才有成就感。昨晚临睡觉前开始写的,今天早起继续写,居然一交就AC了,呵呵,开心????
一、题目大意
给定一个正整数n,求1到n的数字中,所有数含1的数量。
Sample:
12
5
赠送样例:
123
57
二、解题思路
递归枚举每一位+记忆化存储
例如n=123,将n存到vector<int>v
中,v={1, 2, 3}, 从高位开始枚举,最高位下标pos=0,小于等于n的数字中,最高位的值i可以取0、1,也就是可以取[0, v[pos]],而当i==v[pos],它的下一位pos只能取[0, v[pos+1]], i!=v[pos]的话,下一位pos可以取[0, 9]。两者上限的不同是由于当前位的值i是否等于v[pos]引起的,当i==v[pos]
时,当前位已经达到了这个位的上限,使下一位pos+1位最多取到n中pos+1位的值,也就是下一位最多可以取[0, v[pos+1]], 如果超过v[pos+1],则数会超过n,不符合要求。当i!=v[pos]
时,说明当前位的值并没有达到该位的上限,下一位则可以任意取[0, 9]中的数字。
注意点:
- 使用64位整数,32位会爆。
- 使用记忆化存储,加快递归搜索的速度。定义一个三维数组:dp[pos][val][limit],存储当前位为pos, 值为val, limit表示当前位是否受限。当遍历到pos, val, limit的时候,先看dp数组中是否已经存在该值,如果不存在则进行dfs递归搜索遍历,遍历完计算出结果后,存入到dp中记下来,供下一次使用。如果存在dp值,则直接返回该dp值。
- 如果当前位的值为1,则需要累加这位为1时多产生的1的数量,如果不受限,则加pow(10, v.size() - pos - 1),如果受限则加当前位后面所有数字构成的整数。
三、AC代码
#include<bits/stdc++.h>
using namespace std;
using ll = unsigned long long;
int n;
vector<int>v;
void init(){
while(n){
v.push_back