大白书组合基础
输入两个非负整数m和n(m<=n<2^31),求将m~n的所有整数写出来,一共要写多少个数字0?
分析:我自己做时,按照传统数位DP的思想,暴力地用排列组合计算0的个数。
更优的做法,计算每位上为0时,数字的个数。
如果第k位本来就是0,为了让数不大于原本的数:左边取[1,a-1]时,右边可以取任意值;左边取a时,右边只能取0到右边最大;
如果第k位非0,让该位为0,那么左边可以取[1,a],右边可以取任意取。
代码:(更优与自己的都附上)
更优的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long ll;
using namespace std;
ll n, m;
ll cal(ll x) {
if (x < 0)
return 0;
ll ans = 1, cnt = 1, tmp = 0;
while (x >= 10) {
ll cur = x % 10;
x /= 10;
if (cur)
ans += x * cnt;
else ans += (x-1) * cnt + tmp + 1;
tmp += cur * cnt;
cnt *= 10;
}
return ans;
}
int main() {
while (scanf("%lld%lld", &n, &m) != EOF && n != -1 && m != -1) {
printf("%lld\n", cal(m) - cal(n-1));
}
return 0;
}
自己的代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#define MOD 1000000009
using namespace std;
long long d[20],c[20][20],s[20]={0};
void initi()
{
d[0]=1;
for(int i=1;i<=10;i++)
d[i]=d[i-1]*9;
for(int i=0;i<=10;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
}
long long solve(long long n)
{
if(n<0)
return 0;
long long sum=1;
long long a[20]={0},len=0,cnt=0;
while(n)
{
a[len]=n%10;
n/=10;
len++;
}
for(long long i=1;i<len;i++)
{
for(long long j=1;j<i;j++)
sum+=j*c[i-1][j]*d[i-j];
}
for(long long i=len-1;i>=0;i--)
{
if(a[i]!=0)
{
for(long long j=0;j<=i;j++)
{
sum+=((a[i]-1)*(cnt+j))*c[i][j]*d[i-j];
if(i!=len-1)
sum+=(cnt+j+1)*c[i][j]*d[i-j];
}
}
else
cnt++;
}
sum+=cnt;
return sum;
}
int main()
{
long long n,m,i,j;
initi();
while(scanf("%lld %lld",&m,&n)&&m>=0&&n>=0)
{
printf("%lld\n",solve(n)-solve(m-1));
}
return 0;
}