数位dp 总结 51nod数字1的数量 数字0-9的数量

来源:互联网 发布:随机森林算法 原理图 编辑:程序博客网 时间:2024/05/21 10:07

这是一道数位dp题,以前没见过,看了一下别人的想法,终于明白了。

如12可以分为f(9)(1-9的1的个数)+f(2)(1-2的1的个数) +最高位出现1的个数。不过值得注意的是这里最高位是否为1需要判断,决定了其前一位的重复个数,以及最高位的个数。

详细看下:


接着就传统的dp思路:

#include<cstdio>#include<cstring>#include<iostream>#include<cmath>using namespace std;int n;char s[10];int f(int n){///每次获取首位元素    if(n==0)        return 0;    else if(1<=n&&n<=9)        return 1;    sprintf(s,"%d",n);    int bg=s[0]-'0';    int dig=0;    for(int i=0;s[i]!='\0';i++)        dig++;    if(bg==1){     return f(pow(10,dig-1)-1)+f(n-pow(10,dig-1))+n-pow(10,dig-1)+1;    }    else      return bg*f(pow(10,dig-1)-1)+f(n-bg*pow(10,dig-1))+pow(10,dig-1);}int main(){    while(scanf("%d",&n)!=EOF){    printf("%d\n",f(n));    }    return 0;}


以上这种做法对于这道题有点取巧,而真正的数位DP的模板应该如下:

#include<cstdio>#include<cstring>#include<iostream>using namespace std;int c[10];int n;void dp(int n,int d){//n为当前数字,d为当前最低位所代表的进位(如个位对应进位为1,十位进位为10...int h=n/10,l=n%10,temp=h;//h代表的是除去当前数字的最低位所剩下的高位,l则是当前数字的最低位    for(int i=0;i<=l;i++)        {c[i]+=d;/*cout<<"c:"<<c[i]<<endl;*/}//把最低位出现的数字先记录下来,要对应进位,因为如果最低位是个位,则每一个数字的出现只对应一位,而如果是十位,每个数字的出现就对应有10个(如,10,11,12...19)         c[i]+=d*h;//再计算对应低位每个数字之前出现的次数        c[0]-=d;//0这个地方多算了要减去因为像00 01这样的数是不存在的    while(temp){        c[temp%10]+=(l+1)*d;///再计算当前数字较高位对应出现的次数如23 对应20 21 23 2出现了三次        temp/=10;///不断地往较高位走    }    if(h)        dp(h-1,d*10);///计算高位的前一个数 比如23 此时就是计算1开头的情况}int main(){    while(scanf("%d",&n)!=EOF){     memset(c,0,sizeof(c));     dp(n,1);     printf("%d\n",c[1]);    }    return 0;}

然后进阶做一下对应的拓展练习:数字0-9的数量

#include<cstdio>#include<cstring>using namespace std;const int maxn=1e8+50;long long a[10],b[10];long long l,r;void dp(long long t,long long *c,long long d){    long long n=t/10,m=t%10;//n=1,m=9    long long temp=n;//temp=1    for(int i=0;i<=m;i++)        c[i]+=d;    for(int j=0;j<=9;j++)        c[j]+=d*n;    c[0]-=d;    while(temp){        c[temp%10]+=(m+1)*d;        temp/=10;    }    if(n)        dp(n-1,c,d*10);}int main(){    while(scanf("%lld%lld",&l,&r)!=EOF){        memset(a,0,sizeof(a));        memset(b,0,sizeof(b));        dp(l-1,a,1);        dp(r,b,1);        for(int i=0;i<=9;i++)            printf("%lld\n",b[i]-a[i]);    }    return 0;}

虽然对于数位DP还是存在一些疑惑,但是基本上搞懂了这下,不过下面那道幸运区间感觉真的不怎么友好

原创粉丝点击