• P2602 [ZJOI2010]数字计数 (数位dp or 思维)


    题目描述

    给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

    输入输出格式

    输入格式:

    输入文件中仅包含一行两个整数a、b,含义如上所述。

    输出格式:

    输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

    输入输出样例

    输入样例#1: 复制
    1 99
    
    输出样例#1: 复制
    9 20 20 20 20 20 20 20 20 20
    

    说明

    30%的数据中,a<=b<=10^6;

    100%的数据中,a<=b<=10^12。






    历时五个小数,终于知道题解在写什么了,泪目

    找了很多这个题的题解,都是马马虎虎,完全不是给我这种蒟蒻看的好吗?qwq

    抄的题解::::::

    那么我们首先看题,对于这道题我一开始以为很水,但是当我仔细去读题之后发现事情没那么简单。其中对于这道题(递推做法)最大的难点是难以找出递推式子(废话,你写递推就只有这点难了)。为啥,因为你很难想到怎样求出第几位它的数字又多少,因为不能有前导0。但是我们发现如果不考虑是否有前导0的话,那么这道题就似乎有递推公式。

    f[i]代表在有i位数字的情况下,每个数字有多少个。如果不考虑前导0,你会发现对于每一个数,它的数量都是相等的,也就是f[i]=f[i-1]*10+10^(i-1);(这里我推荐使用打表+大眼观察法)

    然而这个公式推出来后,你就会面临第二个难题,怎么推出我想要的答案?

    我们先设数字为ABCD

    看A000,如果我们要求出它所有数位之和,我们会怎么求?

    鉴于我们其实已经求出了0~9,0~99,0~999。。。上所有数字个数(f[i],且没有考虑前导0)我们何不把这个A000看成0000~1000~2000...A000对于不考虑首位每一个式子的数字的出现个数为 A*f[3]。加上首位出现也就是小于A每一个数都出现了10^3次,再加上,我们就把A000处理完了。

    这样你以为就把第一位处理完了?不不不,首位A还出现了BCD+1次呢,也就是从A000~ABCD,这个A还出现了BCD+1次,所以再加上这些才行呢。那么你发现,我们成功把首位代表的所有数字个数求出来了,剩下的求解与A完全没有任何关系,只是BCD的求解,于是我们发现我们已经把一个大问题,化成了一个个小问题,也即是,对于一个这样n位的数,把他一位位的分离开来。

    当然你还需要处理前导0你会发现前导0一定是0001,0002。。。0012,0013。。。0101,0102.。。0999这样的数,一共出现了10*(i-1)+10*(i-2)+...10 (i表示数字位数),让0的统计减去这个值,那么恭喜你这道题做完了。

    总结 对于DP这个东西,最重要的其实只有一点,推状态,状态又是什么?是大问题的子问题,对于这种题最重要的特点是,无后效性,问题可拆分,并且答案的求解具有一定的规律,这样的题应该就可以用DP做,数位DP最重要的就是把一整个数字拆分成一位一位的单独来看,那么对于数位DP,它的子问题也就一般是每一位上对于答案的求解,层层递进的这么一个思路。

    从最高位开始,是为了在最高位是最大数基础上在进行数字的统计!







     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 long long a,b;
     4 long long ten[20],f[20];
     5 long long cnta[20],cntb[20];
     6 void solve(long long x,long long *cnt)
     7 {
     8     long long num[20]={0};
     9     int len=0;
    10     while(x)
    11     {
    12         num[++len]=x%10;
    13         x=x/10;
    14     } 
    15     for(int i=len;i>=1;i--)
    16     {
    17         for(int j=0;j<=9;j++)
    18         cnt[j]+=f[i-1]*num[i];
    19         for(int j=0;j<num[i];j++)
    20         cnt[j]+=ten[i-1];
    21         long long num2=0;
    22         for(int j=i-1;j>=1;j--)
    23         {
    24             num2=num2*10+num[j];
    25         }
    26         cnt[num[i]]+=num2+1;
    27         cnt[0]-=ten[i-1];
    28     } 
    29 }
    30 int main()
    31 {
    32     scanf("%lld %lld",&a,&b);
    33     ten[0]=1;
    34     for(int i=1;i<=15;i++)
    35     {
    36         f[i]=f[i-1]*10+ten[i-1];
    37         ten[i]=10*ten[i-1];
    38     }
    39     solve(a-1,cnta);
    40     solve(b,cntb);
    41     for(int i=0;i<=9;i++)
    42     printf("%lld ",cntb[i]-cnta[i]);
    43 }
  • 相关阅读:
    形象理解ERP(转)
    禁用windows server 2008 域密码复杂性要求策略
    How to adding find,filter,remove filter on display method Form
    Windows Server 2008 R2激活工具
    How to using bat command running VS development SSRS report
    Creating Your First Mac AppGetting Started
    Creating Your First Mac AppAdding a Track Object 添加一个 Track 对象
    Creating Your First Mac AppImplementing Action Methods 实现动作方法
    Creating Your First Mac AppReviewing the Code 审查代码
    Creating Your First Mac AppConfiguring the window 设置窗口
  • 原文地址:https://www.cnblogs.com/zhangbuang/p/10555068.html
Copyright © 2020-2023  润新知