• BZOJ 1833 数字计数 数位DP


    题目链接

    做的第一道数位DP题,听说是最基础的模板题,但还是花了好长时间才写出来。。。。。

    想深入了解下数位DP的请点这里

    先设dp数组dp[i][j][k]表示数位是i,以j开头的数k出现的次数

    有 

        

    数位dp的题一般都会用到前缀数组,题目要求我们求b-a这个区间里各个数码出现的次数,我们可以分别求出(0,b)和(0,a-1)然后相减即可

    具体分析请看代码,写的还算详细

     1 #include <bits/stdc++.h>
     2 using  namespace  std;
     3 typedef long long ll;
     4 ll dp[15][10][10];
     5 ll ans[10];//用来储存每个数码出现的次数 
     6 ll bin[15];//表示i位数中数码i出现的次数 
     7 ll d[15];//这个用来存储数的每一位 
     8 void rule(){
     9     bin[1]=1;
    10     for(int i=2;i<14;i++) bin[i]=bin[i-1]*10;
    11     for(int i=0;i<10;i++) dp[1][i][i]=1;
    12     for(int i=2;i<=13;i++){
    13         for(int j=0;j<=9;j++){
    14             for(int z=0;z<=9;z++){
    15                 for(int k=0;k<=9;k++)
    16                 dp[i][j][z]+=dp[i-1][k][z];//比如i=2,j=2时,所求数字范围应该是200-299,这一步把0-99中各数码出现次数加进去 
    17                 dp[i][z][z]+=bin[i-1];//这一位这是把200-299总共出现了100次的2给加进去 
    18             }
    19         }
    20     }
    21 }
    22 void solve(ll x,int flag){
    23     ll tmpx=x;//存储传进来的x 
    24     int cnt=0;//记录x的位数 
    25     memset(d,0,sizeof(d));
    26     while(x){
    27         d[++cnt]=x%10;
    28         x/=10;
    29     }
    30     for(int i=1;i<cnt;i++){//这一步是最高位为0的,这些数都不会受到x的上限限制,都可以直接加进来 
    31         for(int j=1;j<=9;j++){
    32             for(int k=0;k<=9;k++)
    33             ans[k]+=dp[i][j][k]*flag;
    34         }
    35     }
    36     int tmpt=cnt;
    37     while(cnt){//可以举个456的例子来仔细分析一下 
    38         for(int i=0;i<d[cnt];i++){//注意这里是小于不是等于,保证上限不被取到,在后面再被处理 
    39             if(!i&&cnt==tmpt) continue;//这种情况在最高位为0时已经统计过了,不能重复 
    40             for(int j=0;j<=9;j++){
    41                     ans[j]+=dp[cnt][i][j]*flag;//不是上限的时候都直接加 
    42             }
    43         }
    44         ans[d[cnt]]+=(tmpx%bin[cnt]+1)*flag;cnt--;//随着while循环,上限一步步的被处理 
    45     }//简述一下过程(456),就是第一个for处理了0-399,然后把首位4的57次加上,第二个for,处理的0-49......就这样一步步往下 
    46 }
    47 int  main(){  
    48   ll a,b;scanf("%lld%lld",&a,&b);
    49   rule();
    50   solve(b,1);solve(a-1,-1);//1和-1是符号位,分别是加和减 
    51   for(int i=0;i<10;i++)
    52   printf("%lld%c",ans[i],i==9?'
    ':' ');    
    53   return 0;
    54 }
  • 相关阅读:
    网络编程 Linux IP地址、子网掩码、网关设置和获取
    RFC 3021 Using 31Bit Prefixes on IPv4 PointtoPoint Links
    MySQL SSL 加密连接浅析
    四两拨千斤 —— Ubuntu kernel eBPF 0day分析[腾讯安全应急响应中心20180408]
    supervisor中group与program同时存在,部分program在gunicorn中不启动
    阅读习惯
    网络对抗技术Exp2后门原理与实践
    攻击树测试
    密码常识测试
    网络对抗技术Exp1逆向破解实验
  • 原文地址:https://www.cnblogs.com/qingjiuling/p/10369535.html
Copyright © 2020-2023  润新知