UVA 1640 - The Counting Problem (数位DP)

题目链接 https://cn.vjudge.net/problem/UVA-1640

【题意】
给出整数a,b,统计区间[a,b]中数字0,1,2,3…9分别出现了多少次(1<=a,b<=1e8)

【思路】
计数或数位DP,和51Nod 1042是同一道题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll pw[20];
int a[20];
ll dp[20][15];

ll dfs(int pos,int pre,int k,bool lead,bool limit){
    if(pos==-1){
        if(!lead || 0!=k) return pre==k?1:0;
        else return 0;
    }
    if(!lead && !limit && dp[pos][pre]!=-1) return dp[pos][pre];

    int up=limit?a[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;++i){
        if(pre==k){
            if(!lead || 0!=k){
                if(i==up && limit){
                    ll tmp=0;
                    for(int j=pos-1;j>=0;--j){
                        tmp=tmp*10+a[j];
                    }
                    ans+=tmp+1;
                }
                else{
                    ans+=pw[pos];
                }
            }
            ans+=dfs(pos-1,i,k,i==0 && lead,i==up && limit);
        }
        else{
            ans+=dfs(pos-1,i,k,i==0 && lead,i==up && limit);
        }
    }
    if(!lead && !limit) dp[pos][pre]=ans;
    return ans;
}

ll solve(int x,int k){
    int pos=0;
    while(x){
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,10,k,k==0,true);
}

int main(){
    pw[0]=1;
    for(int i=1;i<=18;++i) pw[i]=pw[i-1]*10;
    int le,ri;
    while(scanf("%d%d",&le,&ri)==2){
        if(0==le && 0==ri) break;
        if(le>ri) swap(le,ri);
        for(int i=0;i<=9;++i){
            memset(dp,-1,sizeof(dp));
            ll x=solve(ri,i);
            memset(dp,-1,sizeof(dp));
            ll y=solve(le-1,i);
            if(i==0) printf("%lld",x-y);
            else printf(" %lld",x-y);
        }
        puts("");
    }
    return 0;
}
posted @ 2018-08-28 15:45  不想吃WA的咸鱼  阅读(432)  评论(0编辑  收藏  举报