poj 2282 The Counting Problem

http://poj.org/problem?id=2282

 

统计[l,r]中每个数码出现的次数

 

一个数位dp,之前做的方法是

dp[i][j]表示到第i位,出现了某个数码j次

对每个数码做一遍,然后再去除前导零

比较麻烦

 

今天发现新思路

f[i][0/1][0/1]表示 dfs到第i位之后,是否有上界限制,是否有前导零,填某个数码的数字个数

g[i][0/1][0/1][j]表示 dfs到第i位之后,是否有上界限制,是否有前导零,数码j的出现次数

 

#include<cstdio>
#include<cstring>

using namespace std;

int tim,vis[10][2][2];
long long dp[10][2][2][10],f[10][2][2],ans[10];

int a[10];

void dfs(int dep,int lim,int zero)
{
    if(vis[dep][lim][zero]==tim) return;
    vis[dep][lim][zero]=tim;
    if(!dep)
    {
        f[dep][lim][zero]=1;
        return;
    }
    int up=lim ? a[dep] : 9,nl,nz;
    for(int i=0;i<=up;++i)
    {
         nl=lim && i==a[dep];
         nz=zero && !i; 
         dfs(dep-1,nl,nz);
         f[dep][lim][zero]+=f[dep-1][nl][nz];
         if(!nz) dp[dep][lim][zero][i]+=f[dep-1][nl][nz];
         for(int j=0;j<=9;++j) dp[dep][lim][zero][j]+=dp[dep-1][nl][nz][j];
    }
}

void solve(int n,int ty)
{
    int len=0;
    while(n) a[++len]=n%10,n/=10;
    tim++;
    memset(f,0,sizeof(f));
    memset(dp,0,sizeof(dp));
    dfs(len,1,1); 
    for(int i=0;i<=9;++i) ans[i]+=ty*dp[len][1][1][i];
}

int main()
{
    int l,r;
    while(1)
    {
        scanf("%d%d",&l,&r);
        if(!l && !r) return 0;
        if(l>r) l^=r,r^=l,l^=r; 
        memset(ans,0,sizeof(ans));
        solve(r,1);
        solve(l-1,-1);
        for(int i=0;i<=9;++i) printf("%lld%c",ans[i],i==9 ? '\n' : ' '); 
    } 
}

 

posted @ 2020-02-27 16:30  TRTTG  阅读(168)  评论(0编辑  收藏  举报