题目链接:http://acm.tju.edu.cn/toj/showp4148.html
题意是算出一个数小于等于它的所有数中0的个数。
首先上来第一反应是暴力,毕竟每一个数的答案都是死的,不会随着变量变化,所以自己写了一个10^8的暴力,抱着一秒一亿次运算可能能卡过的侥幸心里,想试一试,不过结果自己运行都要差不多10秒才能跑出来……
那么我们只能尝试去找寻规律之类的做法,那么我们可以考虑每一位上的关系(也就是所谓的数位DP)。
我们从第一位开始考虑每一位出现0的个数的情况,举个例子,我们来讨论1234这个数不超过它出现的0的个数。
从个位开始,如果这位是0的话,那么前面的三位可以从1~123都可以,所以个位就有123个0;
十位,如果这位是0的话,那么前面的可以从1~12,后面的可以从0~9,所以十位就有120个0;
百位,如果这位是0的话,那么前面的只能排1,后面的可以从0~99,所以百位就有100个0;
所以1234总共就有123+120+100 = 323个0;
但是我们刚刚讨论的情况不够特殊,因为不含0,但是实际讨论的时候如果有0的话我们要特殊讨论一下,再举个例子把刚刚的1234换成1204。
个位:1~120,120个0;
十位:前面是1~12,后面是0~9,这么看来应该是120个,但是当前面的是120的时候,如果我们后面的比4大的话,那么我们枚举的这个实际就超过了1204,我们只能枚举小于1204的,所以十位应该是120 - (10-4-1) = 115.
百位:前面只能是1,后面是0~99,100个0:
所以总共就是120+115+100 = 335个0.
那么从刚刚两个例子我们可以看出来,在枚举的过程中,如果枚举的那一位是非0的话,那么这一位的0的个数就是前面的数*后面的数,如果这一位是0的话,那么我们还要讨论枚举出来的比这个大的情况,这一位0的个数是前面的数*后面的数 - 枚举多出来的数。
#include <cstdio>
long Count(long n)
{
long count = 0;
long i = 1;
long current = 0, after = 0, before = 0;
while((n / i) != 0)
{
current = (n / i) % 10;
before = n / (i * 10);
after = n - (n / i) * i;
if(current > 0)
count = count + before * i;
else if(current == 0)
count = count + before * i - (i-after-1);
i = i * 10;
}
return count;
}
int main()
{
int n,T;
scanf("%d",&T);
while(T--)
{
scanf("%d", &n);
int x = Count(n);
printf("%d\n",x);
}
}