51Nod 1042 - 数字0-9的数量(计数/数位DP)
题目链接 https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1042
【题目描述】
给出一段区间 [a,b],统计这个区间内0-9出现的次数
比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次
Input
两个数
Output
输出共10行,分别是0-9出现的次数
Input示例
10 19
Output示例
1
11
1
1
1
1
1
1
1
1
【思路】
51Nod 1009的进阶版,可以计数,可以数位DP,注意0比较特殊,因为前导0是不能被计算在内的,下面是计数直接计算
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b;
ll c[20];
ll pow(ll x, int n)
{
ll res = 1;
while (n)
{
if (n & 1) res *= x;
x *= x;
n >>= 1;
}
return res;
}
void init()
{
memset(c, 0, sizeof(c));
for (int i = 1; i <= 18; i++)
c[i] = i*pow(10, i - 1);
}
ll solve(ll x, int num)//返回0~x中数字num出现的次数
{
ll res = 0, copy = x;
ll total = 0, po = 1;//total不断求和,po表示当前的幂
int len = 0;//由低位向高位求到的当前某一位
int dig;//当前位
while (x)
{
dig = x % 10;
x /= 10;
len++;
if (dig < num) res += dig*c[len - 1];
else if (dig == num) res += dig*c[len - 1] + 1 + total;
else res += dig*c[len - 1] + po;
total += dig*po;
po *= 10;
}
if (0 == num)
{
ll temp = 1;
while (copy)
{
res -= temp;
temp *= 10;
copy /= 10;
}
res++;
}
return res;
}
int main()
{
init();
while (~scanf("%lld%lld", &a, &b))
{
for (int i = 0; i <= 9; i++)
printf("%lld\n", solve(b, i) - solve(a - 1, i));
}
return 0;
}
数位DP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll pw[20];
int a[20];
ll dp[20][15];
ll dfs(int pos,int pre,int k,bool lead,bool limit){
if(pos==-1){
if(!lead || k!=0) return pre==k?1:0;
else return 0;
}
if(!lead && !limit && dp[pos][pre]!=-1) return dp[pos][pre];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;++i){
if(pre==k){
if(k!=0 || !lead){
if(i==up && limit){
ll tmp=0;
for(int j=pos-1;j>=0;--j){
tmp=tmp*10+a[j];
}
ans+=tmp+1;
}
else{
ans+=pw[pos];
}
}
ans+=dfs(pos-1,i,k,i==0 && lead,i==up && limit);
}
else{
ans+=dfs(pos-1,i,k,i==0 && lead,i==up && limit);
}
}
if(!lead && !limit) dp[pos][pre]=ans;
return ans;
}
ll solve(ll x,int k){
int pos=0;
while(x){
a[pos++]=x%10;
x/=10;
}
return dfs(pos-1,10,k,k==0,true);
}
int main(){
pw[0]=1;
for(int i=1;i<=18;++i) pw[i]=10*pw[i-1];
ll le,ri;
scanf("%lld%lld",&le,&ri);
for(int k=0;k<=9;++k){
memset(dp,-1,sizeof(dp));
ll x=solve(ri,k);
memset(dp,-1,sizeof(dp));
ll y=solve(le-1,k);
printf("%lld\n",x-y);
}
return 0;
}