UVA 1640 - The Counting Problem (数位DP)
题目链接 https://cn.vjudge.net/problem/UVA-1640
【题意】
给出整数a,b,统计区间[a,b]中数字0,1,2,3…9分别出现了多少次(1<=a,b<=1e8)
【思路】
计数或数位DP,和51Nod 1042是同一道题
#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 || 0!=k) 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(!lead || 0!=k){
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(int 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]=pw[i-1]*10;
int le,ri;
while(scanf("%d%d",&le,&ri)==2){
if(0==le && 0==ri) break;
if(le>ri) swap(le,ri);
for(int i=0;i<=9;++i){
memset(dp,-1,sizeof(dp));
ll x=solve(ri,i);
memset(dp,-1,sizeof(dp));
ll y=solve(le-1,i);
if(i==0) printf("%lld",x-y);
else printf(" %lld",x-y);
}
puts("");
}
return 0;
}