【1,n】区间内或者【a,b】区间内的具有某种性质的数的个数如何统计?
此类问题往往分为以下几类:
1、统计单个数出现的次数,如:【10,1000】中数字0/1/2/3...出现的次数
2、统计某两位数出现的次数,如:【10,1000】中49出现的次数
3、统计具有某种特殊性质的数有多少个,如:【10,1000】中含有13并且能被13整除的数...
从第一类开始,由易到难
例1: 51 nod 1009
【法一】:直接计数(组合数学)
求【1,N】中1出现的个数规律如下:
如果当前位小于1,那么个数等于高位*当前位率
如果当前位等于1,那么个数等于高位*当前位率+低位+1
如果当前位大于1,那么个数等于(高位+1)*当前位率
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int getnum(int n,int i)
{
int res=0;
int factor=1;
while(n/factor) //当前位数
{
int high=(n/factor)/10; //高位
int curr=(n/factor)%10; //当前位
int low=n-(n/factor)*factor; //低位
if(curr<i)
res=res+(high)*factor;
else if(curr==i)
res=res+(high)*factor+low+1;
else if(curr>i)
res=res+(high+1)*factor;
factor=factor*10;
}
return res;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
printf("%d\n",getnum(n,1));
}
return 0;
}
【法二】:动态规划(数位DP)
设置状态:
dp数组表示【1,以j开头的i位数 】的区间内 1 出现的次数
如dp[1][1]表示以1开头的一位数,即【1,1】中1出现的次数
dp[2][2]表示以2开头的两位数,即【1,29】中1出现的次数
dp[2][3]表示以3开头的两位数,即【1,39】中1出现的次数
状态转移方程:
if(j!=1) dp[i][j]=dp[i][j-1]+dp[i-1][9];
else dp[i][j]=2*dp[i-1][9]+date;
预处理dp数组:
如果不是1开头,则为j的上一个状态加i的上一个状态
如dp[2][3]即【1,39】=【1,29】+【1,9】
如dp[4][5]即【1,5999】=【1,4999】&#