给定一个十进制正整数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
问题链接:51Nod-1009 数字1的数量
问题分析:
这是一个数位DP问题,用记忆化搜索实现。
扫描二维码关注公众号,回复:
3079893 查看本文章
程序说明:(无)
题记:(略)
AC的C++语言程序如下:
/* 51Nod-1009 数字1的数量 */
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10; // 位数,int类型不超过10位
int digits[N + 1];
LL dp[N][N]; // dp[i][d]-共i位,其中1的个数为d数的数量
/*
* 参数:
* pos - 数位位置,即当前处理数的第几位,从高位开始
* cnt - 1的个数
* limit - 是否为数位上界(最大数字)
*/
int dfs(int pos, int cnt, bool limit)
{
if(pos == -1) // 递归边界,已经枚举结束,则返回1的数量
return cnt;
if(!limit && dp[pos][cnt] != -1) // 已经搜索过的不再搜索,直接使用之前的计算结果
return dp[pos][cnt];
// 计数
int ans = 0;
int maxd = limit ? digits[pos] : 9; // 枚举数字,如果数字不同则枚举0-9
for(int i = 0; i <= maxd; i++) {
if(i == 1)
ans += dfs(pos - 1, cnt + 1, limit && i == digits[pos]);
else
ans += dfs(pos - 1, cnt, limit && i == digits[pos]);
}
if(!limit)
dp[pos][cnt] = ans;
return ans;
}
// 计算[0,n]中各个数的1的数量之和
int solve(int n)
{
int len = 0;
while(n) {
digits[len++] = n % 10;
n /= 10;
}
return dfs(len - 1, 0, 1);
}
int main()
{
memset(dp, -1, sizeof(dp));
int n;
while(~scanf("%d", &n))
printf("%d\n", solve(n));
return 0;
}