count 数字计数 (数位dp)
原创
©著作权归作者所有:来自51CTO博客作者是那北方的风的原创作品,请联系作者获取转载授权,否则将追究法律责任
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
Input
输入文件中仅包含一行两个整数a、b,含义如上所述。
Output
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。
Sample Input
1 99
Sample Output
9 20 20 20 20 20 20 20 20 20
Hint
30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。
题目大概:
题目很精简。
思路:
做一个结构题存储所有的0到9的数的个数,并储存现在位的个数。
每次统计是把现在位的个数储存,并把以前存储的数字的数量一起进行赋值。
这个题主要是统计10位数,麻烦一点。
代码:
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
int a[22];
struct DP
{
ll wei[10],sum;
DP(){memset(wei,0,sizeof(wei));}
DP(ll su):sum(su){memset(wei,0,sizeof(wei));}
}dp[20][2],ans[2];
DP sove(int pos,int lead,int limit)
{
if(pos==-1)return DP(1);
if(!limit&&~dp[pos][lead].sum)return dp[pos][lead];
int end=limit?a[pos]:9;
DP now(0);
for(int i=0;i<=end;i++)
{
DP t=sove(pos-1,lead&&!i,limit&&i==a[pos]);
for(int j=0;j<10;j++)
{
now.wei[j]+=t.wei[j];
}
now.sum+=t.sum;
if(!lead||lead&&i)now.wei[i]+=t.sum;
}
if(!limit)dp[pos][lead]=now;
return now;
}
void go(ll x,int k)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/=10;
}
ans[k]=sove(pos-1,1,1);
}
int main()
{
ll m,n;
for(int i=0;i<20;i++)
{
for(int j=0;j<=1;j++)
{
dp[i][j].sum=-1;
}
}
while(cin>>m>>n)
{
memset(ans,0,sizeof(ans));
go(n,0);
go(m-1,1);
for(int i=0;i<=9;i++)
{
if(i!=9)printf("%lld ",ans[0].wei[i]-ans[1].wei[i]);
else printf("%lld\n",ans[0].wei[i]-ans[1].wei[i]);
}
}
return 0;
}