数位统计DP——计数问题

原题传送门

解析

啊啊啊!这道题卡了一下午才搞出来,巨多边界问题!

对于直接求解比较困难的问题,可以转化成前缀的方式求解,这道题也是一样,可以求解1~b 中数x出现的数量,减去1~a-1中数x出现的数量,间接得到答案

假定一个数按位表示成abcdefg,指针指到d这一位,统计这种情况下的答案
当x不为0的时候
1、当d这一位枚举x的时候,abc这三位可以从000~abc-1,efg这三位可以从000~999,也就是1000=103种选择
2、当abc这三位就枚举abc的时候,当d<x,有0种选择
3、当abc这三位就枚举abc的时候,当d=x,efg这三位有000~efg,也就是efg+1种选择
4、当abc这三位就枚举abc的时候,当d>x,efg三位有000~999,也就是1000=103种选择
当x为0的时候
只有1与上述情况不同,因为当d是0的时候不能有前导0存在,因此abc三位只能从001~abc-1范围内进行选择

代码

在代码中会有很多的边界问题存在
当n==0的时候,统计x出现的数量就是0
当统计0出现次数的时候,高位不能是0

#include <bits/stdc++.h>
using namespace std;
int power10(int i)
{
    
    
    int res=1;
    while(i--) res*=10;
    return res;
}
int get(vector<int>vis,int l,int r)
{
    
    
    int res=0;
    for(int i=l;i>=r;i--) res=res*10+vis[i];
    return res;
}
int count(int n,int x) //1~n中数x出现的次数
{
    
    
    if(n==0) return 0; //处理边界
    vector<int>vis;
    while(n)
    {
    
    
        vis.push_back(n%10);
        n/=10;
    }
    int l=vis.size(); //记录这个数一共有多少位
    int res=0;
    for(int i=l-1-!x;i>=0;i--) //当x==0时因为最高位不能是0,因此从第二位开始枚举
    {
    
    
        if(i<l-1) //枚举到最高位的时候,不存在比这一位更高的位
        {
    
    
            res+=get(vis,l-1,i+1)*power10(i);
            if(!x) res-=power10(i); //当x==0时,高位的数要从001开始枚举
        }
        if(vis[i]==x) res+=get(vis,i-1,0)+1;
        else if(vis[i]>x) res+=power10(i);
    }
    return res;
    
}
int main()
{
    
    
    int a,b;
    while(cin>>a>>b&&a!=0&&b!=0)
    {
    
    
        if(a>b) swap(a,b);
        for(int i=0;i<10;i++)
            cout<<count(b,i)-count(a-1,i)<<" ";
        cout<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46126537/article/details/113091972
今日推荐