给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数。
N(1 <= N <= 10^9)
dp[i] 表示 0 ~ (10 ^ i - 1) 中1的个数。
ten[i] 表示 10 ^ i 的值
pos 当前处理的位
num 当前处理的位前面已经有的1个数
limit 当前位有没限制
其实dp可以看作记录过程答案的dfs。(即是记忆化搜索) 可以把这题当作dfs做。
对于 0 ~ 4321 有多少个1呢?
图:
修正:虽然这题过掉了,后来做 hdu 2098 ,根据我自己的理解码代码,无限wa,不知道是错在哪里,后来强行手跑代码,发现先前的理解不准确。
进一步理解:数位dp的记忆化搜索不能理解为边爆搜边记录,这样是没有意义的(之前脑子懵),如上图:它先在最深一层搜10次(0000~0009),记录dp值,运用在最后第二层搜10次(001X~009X)上,以此类推,搞出所有需要的dp值无非只搜了 (位数*10)次;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<map>
#include<queue>
#include<cmath>
#include<algorithm>
#include<deque>
typedef long long LL;
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
const int INF=0x3f3f3f3f;
const int N = 20;
const double eps = 1e-6;
int ten[N]; //10^i 的值
void init()
{
ten[0] = 1;
for(int i = 1; i < 10; i++)
ten[i] = ten[i-1]*10;
}
char s[N];
int len;
int dp[N]; //0->10^i-1 中1的个数
int dfs(int pos,int num, int limit)
{
if(pos == len)
return num;
if(!limit && dp[len-pos-1] != -1)
return num*ten[len-pos]+dp[len-pos-1];
int up = limit ? s[pos]-'0' : 9;
int tmp = 0;
for(int i = 0; i <= up; i++)
tmp += dfs(pos+1,num+(i==1),limit && s[pos]-'0' == i);
if(!limit)
dp[len-pos-1] = tmp;
return tmp;
}
int main()
{
init();
scanf("%s",s);
len = strlen(s);
memset(dp,-1,sizeof(dp));
printf("%d\n",dfs(0,0,1));
return 0;
}