• 几道数位DP


      因为这几天写的几道数位DP大多都太水。。而且也确实没什么好讲所以就扔到一起了。

    [hdu4772]Good Numbers

      要求统计区间内 各位数之和能被10整除 的数的个数。

      练手,f[i][j][k]表示i位的数,以j开头,各位数之和取模10的结果为k,的方案数。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 int i,j,j1,k,k1,n,m;
     7 ll f[19][10][10],ten[19],l,r;
     8 
     9 int s[20],len;
    10 inline ll get(ll x){
    11     if(x<0)return 0;
    12     int i,j,k,pre;ll tmp,ans=0;
    13     for(i=0;i<=18;i++)if(x==ten[i]){x--;break;}
    14     if(!x)return 1;
    15     for(tmp=x,len=0;tmp;tmp/=10)s[++len]=tmp%10;
    16     
    17     for(i=1;i<len;i++)for(j=1;j<=9;j++)ans+=f[i][j][0];
    18     for(i=1;i<s[len];i++)ans+=f[len][i][0];
    19     
    20     for(pre=s[len],i=len-1;i;i--){
    21         k=(-pre+10)%10;
    22         for(j=0;j<s[i];j++)ans+=f[i][j][k];
    23         pre=(pre+s[i])%10;
    24     }
    25     return ans+(pre==0)+1;
    26 }
    27 int main(){
    28     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*10;
    29     for(i=0;i<=9;i++)f[1][i][i]=1;
    30     for(i=2;i<=18;i++)
    31         for(j=0;j<=9;j++)for(k=0;k<=9;k++)
    32         for(j1=0,k1=(k-j+10)%10;j1<=9;j1++)f[i][j][k]+=f[i-1][j1][k1];
    33     
    34     for(scanf("%d",&m),n=1;n<=m;n++){
    35         scanf("%lld%lld",&l,&r);
    36         printf("Case #%d: %lld
    ",n,get(r)-get(l-1));
    37     }
    38     return 0;
    39 }
    View Code

    [hdu2089]不要62

      要求统计区间内有多少个数 不包含62并且不包括4.

      f[i][j]表示i位的数,以j开头,非法数字的个数。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 using namespace std;
     5 int f[10][10],ten[10],sum;
     6 int i,j,k,n,m;
     7 int now[12];
     8 inline int get(int x){
     9     if(!x)return 0;
    10     int i,len=0,ans=0;
    11     for(int tmp=x;tmp;tmp/=10)now[++len]=tmp%10;
    12     now[len+1]=0;
    13     for(i=len;i;i--){
    14         for(j=0;j<now[i];j++){
    15             ans+=f[i][j];//,printf("add:  %d %d
    ",i,j);
    16             if(j==2&&now[i+1]==6)ans+=ten[i-1]-f[i][j];
    17         }
    18         if(now[i]==4||(now[i]==2&&now[i+1]==6)){ans+=x%ten[i-1]+1;break;}
    19     }
    20 //    printf("%d  ans:%d
    ",x,ans);
    21     return ans;
    22 }
    23 int main(){    
    24     for(i=0;i<=9;i++)f[1][i]=i==4;
    25     ten[0]=1,ten[1]=10;
    26     for(i=2;i<=9;i++){
    27         ten[i]=ten[i-1]*10;
    28         for(j=sum=0;j<=9;j++)sum+=f[i-1][j];
    29         for(j=0;j<=9;/*printf("%d %d  %d
    ",i,j,f[i][j]),*/j++)
    30             if(j==4)f[i][j]=ten[i-1];
    31             else if(j==6)f[i][j]=sum+ten[i-2]-f[i-1][2];
    32             else f[i][j]=sum;
    33     } 
    34     while(scanf("%d",&n)==1){
    35         scanf("%d",&m);
    36         if(!n&&!m)break;
    37         printf("%d
    ",m-n+1-(get(m)-get(n-1)));
    38     }
    39     return 0;
    40 }
    View Code

    [hdu3555]bomb

      要求统计区间内有多少个包含49的数。

      f[i][j]表示i位的数,j开头,包含49的数的个数。(因为49这个数比较特殊一点(9是最大的数)...统计的时候可以不写关于第二位数的特判)

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 ll f[20][10],ten[23];
     7 int i,j,len,s[23];
     8 ll tmp,n;
     9 inline ll get(ll x){
    10     ll ans=0;
    11     for(len=0,tmp=x;tmp;tmp/=10)s[++len]=tmp%10;s[len+1]=0;
    12     for(i=1;i<len;i++)for(j=1;j<=9;j++)ans+=f[i][j];
    13     for(i=len;i;i--){
    14         for(j=i==len;j<s[i];j++)ans+=f[i][j];
    15         if(s[i]==9&&s[i+1]==4){ans+=x%ten[i-1]+1;break;}
    16     }
    17     return ans;
    18 }
    19 int main(){
    20     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*(ll)10;
    21     for(i=1;i<=19;i++)
    22         for(j=0;j<=9;/*printf("%d %d  %lld
    ",i,j,f[i][j]),*/j++)
    23         if(j!=4&&i>1)f[i][j]=f[i-1][j]*9+f[i-1][4];
    24         else f[i][j]=f[i-1][1]*8+f[i-1][4]+ten[i-2];
    25     int T;
    26     for(scanf("%d",&T);T;T--)
    27     scanf("%lld",&n),printf("%lld
    ",get(n));
    28     return 0;
    29 }
    View Code

    [hdu4507]恨7不成妻

      预处理的时候,数字的平方和可以通过 方案数、数字和、数字的平方和 三个玩意算出来。。算是常见姿势了吧。

      再具体点见代码注释

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 const int modd=1000000007;
     7 struct zs{
     8     ll num,sum,sqr;//方案数,方案的数字和,平方和
     9 }f[21][10][2][7][7];//f[i][x][j][k][l]:i位的数,开头数字为x,j表示有无某一位为7,k表示每一位加起来的和%7的值,l表示数字%7的值。
    10 ll ten[23];
    11 ll l,r;
    12 
    13 
    14 inline void add(zs &a,zs b,ll num){
    15     num%=modd;
    16     a.num+=b.num,a.sum+=(b.sum+num*b.num)%modd,
    17     a.sqr+=b.sqr+(b.sum*num%modd*2)+(b.num*num%modd*num%modd),
    18     a.num-=a.num>=modd?modd:0,a.sum-=a.sum>=modd?modd:0,
    19     a.sqr%=modd;
    20 }
    21 
    22 int s[20],len;
    23 inline ll get(ll num){
    24     if(num==1)return 0;
    25     register int i,j,k,x,pre2;ll tmp,ans=0,pre,pre1;zs b;
    26     
    27     for(tmp=num,len=0;tmp;tmp/=10)s[++len]=tmp%10;
    28     for(i=1;i<len;i++)for(x=1;x<=9;x++)for(j=1;j<7;j++)for(k=1;k<7;k++)ans+=f[i][x][0][j][k].sqr,ans-=ans>=modd?modd:0;
    29     for(x=1;x<s[len];x++)for(j=1;j<7;j++)for(k=1;k<7;k++)ans+=f[len][x][0][j][k].sqr,ans-=ans>=modd?modd:0;
    30     
    31     pre=s[len]*ten[len-1]%modd,pre1=s[len]*ten[len-1]%7,pre2=s[len]%7;
    32     if(s[len]!=7)
    33     for(i=len-1;i;i--){
    34     //    printf("   i:%d   pre1:%lld   pre2:%d      pre:%lld
    ",i,pre1,pre2,pre);
    35         for(x=0;x<s[i];x++)for(j=1;j<7;j++)for(k=1;k<7;k++)
    36             b=f[i][x][0][(j-pre2+7)%7][(k-pre1+7)%7],
    37             ans=(ans+b.sqr+b.sum*pre%modd*2+b.num*pre%modd*pre%modd)%modd;
    38         pre=(pre+s[i]*ten[i-1])%modd;pre1=(pre1+s[i]*ten[i-1])%7;pre2=(pre2+s[i])%7;
    39         if(s[i]==7)break;
    40     }
    41     return ans;
    42 }
    43 int main(){
    44     register int i,j,k,j1,k1,x;ll num;
    45     
    46     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*10;
    47     for(i=0;i<=9;i++)k=i%7,f[1][i][i==7][k][k]=(zs){1,i,i*i};
    48     for(i=2;i<=19;i++)
    49         for(j=0;j<7;j++)for(k=0;k<7;k++)
    50         for(x=num=0;x<=9;x++,num+=ten[i-1]){
    51             j1=j-x%7,j1+=j1<0?7:0,k1=k-(num%7),k1+=k1<0?7:0;
    52             if(x!=7)
    53                 for(int x1=0;x1<=9;x1++)add(f[i][x][0][j][k],f[i-1][x1][0][j1][k1],num);
    54             else 
    55                 for(int x1=0;x1<=9;x1++)add(f[i][x][1][j][k],f[i-1][x1][0][j1][k1],num);
    56             for(int x1=0;x1<=9;x1++)add(f[i][x][1][j][k],f[i-1][x1][1][j1][k1],num);
    57         }
    58        
    59     int T;
    60     for(scanf("%d",&T);T;T--){
    61         scanf("%lld%lld",&l,&r);
    62         printf("%lld
    ",(get(r+1)-get(l)+modd)%modd);
    63     }
    64     return 0;
    65 }
    View Code

    [hdu3652]B-number

      要求出区间内包含13,并且能被13整除的数的个数。

      f[i][j][k][l]表示i位数,开头为j,k表示是否包含13,l表示数字模13后的结果,的方案数

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 int ten[11];
     7 int f[11][10][2][13];
     8 int i,j,k,j1,k1,n,m;
     9 
    10 int s[12],len;
    11 inline int get(int x){
    12     if(x<13)return 0;
    13     register int i,j,k,j1,k1,ans=0,pre;int tmp;
    14     for(len=0,tmp=x;tmp;tmp/=10)s[++len]=tmp%10;
    15     for(i=1;i<len;i++)for(j=1;j<=9;j++)ans+=f[i][j][1][0];
    16     for(i=1;i<s[len];i++)ans+=f[len][i][1][0];
    17     
    18     pre=(s[len]*ten[len-1])%13;bool flag=0;
    19     for(i=len-1;i;i--){
    20         k=(-pre+13)%13;
    21         if(!flag)
    22             for(j=0;j<s[i];j++)ans+=f[i][j][1][k];
    23         else
    24             for(j=0;j<s[i];j++)ans+=f[i][j][1][k]+f[i][j][0][k];
    25         if(!flag&&s[i+1]==1&&s[i]>3)ans+=f[i][3][0][k];
    26         if(s[i]==3&&s[i+1]==1)flag=1;
    27         pre=(pre+s[i]*ten[i-1])%13;
    28     }
    29     if(flag)ans+=pre==0;
    30     return ans;
    31 }
    32 int main(){
    33     for(i=ten[0]=1;i<=10;i++)ten[i]=ten[i-1]*10;
    34     for(i=0;i<=9;i++)f[1][i][0][i]=1;
    35     for(i=2;i<=10;i++)for(j=0;j<=9;j++){
    36         
    37         for(k=0;k<13;k++){
    38         for(j1=0,k1=(k-j*ten[i-1]%13+13)%13;j1<=9;j1++){
    39             if(j!=1||j1!=3) f[i][j][0][k]+=f[i-1][j1][0][k1];
    40             else f[i][j][1][k]+=f[i-1][j1][0][k1];
    41             f[i][j][1][k]+=f[i-1][j1][1][k1];
    42         }
    43     //    printf("   %d %d %d %d
    ",i,j,k,f[i][j][1][k]);
    44         }
    45     }
    46     while(scanf("%d",&n)==1)
    47         printf("%d
    ",get(n));
    48     
    49     return 0;
    50 }
    View Code

    [hdu3709]Balanced Number

      无力去膜题解。http://blog.csdn.net/acm_cxlove/article/details/7821726

      顺便感受了一下记忆化搜索在处理边界等方面的优越性= =

      感觉for的话就是负数不太好处理。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 int i,j,k,n,m;
     7 int s[19],len;ll tmp,l,r;
     8 ll f[19][19][1600],ten[19];
     9 bool u[19][19][1600];
    10 
    11 inline ll dfs(int x,int pos,int pre,bool limit){//考虑剩下的x位数字,支点已确定在pos
    12     if(!x||pre<0) return pre==0;
    13     if(!limit&&u[x][pos][pre])return f[x][pos][pre];
    14     
    15     int mx=limit?s[x]:9;ll ans=0;
    16     for(int i=0;i<=mx;i++)
    17         ans+=dfs(x-1,pos,pre-i*(pos-x),limit&&(i==mx));
    18     if(!u[x][pos][pre]&&!limit)
    19         f[x][pos][pre]=ans,u[x][pos][pre]=1;
    20     return ans;
    21 }
    22 inline ll get(ll x){
    23     ll ans=0;
    24     for(int i=0;i<=18;i++)
    25     if(x==ten[i]){x--,ans++;break;}
    26     if(x<0)return 0;
    27     
    28     for(len=0,tmp=x;tmp;tmp/=10)s[++len]=tmp%10;
    29     for(int i=len;i;i--)
    30         ans+=dfs(len,i,0,1);
    31     return ans-len+1;
    32 }
    33 int main(){
    34     for(i=ten[0]=1;i<=18;i++)ten[i]=ten[i-1]*10;
    35     int T;
    36     for(scanf("%d",&T);T;T--){
    37         scanf("%lld%lld",&l,&r);
    38         printf("%lld
    ",get(r)-get(l-1));
    39     }
    40     return 0;
    41 }
    View Code

    UPD:其实以上写法中,f 数组的“以数字j开头”的那维都是不必要的。。。(所以我个傻逼预处理比别人多了两重循环TAT

  • 相关阅读:
    企业级应用TOMCAT
    HTTP服务及状态码
    Zabbix服务网页报错汇总
    spring aop 嵌套方法注解不生效
    oracle数据库密码修改和解锁
    redis安装
    docker简介
    ffmpeg简介
    JAVA学习笔记之变量
    JAVA学习笔记之类和对象
  • 原文地址:https://www.cnblogs.com/czllgzmzl/p/5218578.html
Copyright © 2020-2023  润新知