题目链接: pku2282
方法:分治
思想:
本题要求出1在两个数a和b之间出现的次数。可以有分治算法的思想,先求出1在(0,a]之间出现的次数,
再求出1在(0,b]出现的次数,然后两者相减即可。假设N=abcd,这里a,b,c,d分别是千位,百位,十位,个位上的数字。
如果要计算十位上(c)出现1的次数,它将受到三个因素的影响:十位上的数(c),十位以上的数字(ab),十位以下的数字(d)。
if(c==0) f(N) += ab*10; //比如1201
else if(c==1) f(N) += ab*10 + d+1; //比如1211
else f(N) += (ab+1) * 10; //比如1221
更多可以参考 fg0711 ljsspace 和 编程之美132页(1的数目)
代码:
//分治法
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int d[10];//记录数字0-9分别出现的次数
int value;//便于处理相减的情况
//也可以用两个数组分别保存0-a,0-b中相应
//数字出现的情况,然后相减的绝对值便是结果
//这时将value直接替换成1便可。
void deal(int n)
{
if(n<=0)return ;
int i,one,ten;//分别表示个位、十位
one=n%10;
n/=10;ten=n;
for(i=0;i<=one;i++)d[i]+=value;//将个位上出现的数统计下来
while(ten)
{
d[ten%10]+=(one+1)*value;
ten/=10;
}
for(i=0;i<10;i++)d[i]+=n*value;
d[0]-=value;
value*=10;
deal(n-1);
}
int main()
{
int a,b,i;
while(scanf("%d%d",&a,&b)==2&&(a||b))
{
if(a>b){ a=a^b;b=b^a;a=a^b; }
for(i=0;i<10;i++)d[i]=0;
value=1;
deal(b);
value=-1;
deal(a-1);
for(i=0;i<10;i++)
if(i<9)printf("%d ",d[i]);
else printf("%d\n",d[i]);
}
return 0;
}