uva 1640 两种方法 数位dp或枚举计算

来源:互联网 发布:怎么制作视频软件 编辑:程序博客网 时间:2024/05/16 12:40

对于区间计数问题,一般都是令F {i}(n)为[0,n]之间i出现的次数,再去区间相减就可以了

第一种方法:

例如 4321,分别求0-3999,4000-4299,4300-4319,4320+4321

0-3999中,最高位只出现过0,1,2,3, 对应的它出现的次数为1000(x***),其他位上每种数字出现的次数相同,都是(1000*3)/10

前导0不算,为了计算方便,先算进去,再减,不算的情况只出现在了第一次计算0-3999的时候,有多少个呢? 

1000+100+10

所以我们按位从高位开始枚举,来统计各个数字出现的次数

注意 在计算10^n的时候不能用pow转换成整形来计算,会出现精度丢失的情况吗,所以自己生成一个就行了。

#include <iostream>#include <cstdio>#include <ctime>#include <cstring>#include <algorithm>#include <vector>#include <map>#include <cmath>#include <set>#include <queue>using namespace std;const int INF=1e9+1000;const double EPS = 1e-10;  typedef long long ll;int a[15],b[15];int pova[17];int mpow10[10];void cal(int n,int *d){    int tmp=n;    int pos=0;    int c[15];    do{        c[pos++]=tmp%10;        tmp/=10;      }while(tmp);    pos--;    char s[15];    sprintf(s,"%d",n);    for(int i=0;i<=pos;i++){        sscanf(s+pos-i,"%d",&pova[i]);    }    int k=mpow10[pos];    for(int i=pos;i>0;i--){        for(int j=0;j<c[i];j++){            d[j]+=k;            for(int z=0;z<=9;z++){                d[z]+=k*i/10;            }        }        d[c[i]]+=pova[i-1]+1;        d[0]-=k;        k/=10;            }        for(int i=0;i<=c[0];i++)        d[i]++;}int main(){    //freopen("out.txt","w",stdout);    //ios_base::sync_with_stdio(false);    int l,r;    mpow10[0]=1;    for(int i=1;i<=8;i++)        mpow10[i]=mpow10[i-1]*10;    while(scanf("%d %d",&l,&r)&&l+r){        if(l>r) swap(l,r);        memset(a,0,sizeof(a));        memset(b,0,sizeof(b));        cal(l-1,a);        cal(r,b);        for(int i=0;i<=9;i++)            printf("%d%c",b[i]-a[i],i==9?'\n':' ');    }    return 0;}


第二种方法,数位dp,就是记忆化搜索,对于重复计算的状态,存在数组中就可以直接返回了。

其中  cnt的设置很是精妙,避免了一大堆的判定,lead用来判断枚举到该位的时候是否有前导0,。

#include <iostream>#include <cstdio>#include <ctime>#include <cstring>#include <algorithm>#include <vector>#include <map>#include <cmath>#include <set>#include <queue>using namespace std;const int INF=1e9+1000;const double EPS = 1e-10;  typedef long long ll;int a[15],b[15];int digit[15];int d[15][2][15][15];int dfs(int pos,int lead,int val,int limit,int cnt){if(pos==-1){if(lead)return 1;elsereturn cnt;}if(!limit&&d[pos][lead][val][cnt]!=-1) return d[pos][lead][val][cnt];int up=limit?digit[pos]:9;int ans=0;for(int i=0;i<=up;i++){if(!lead){ans+=dfs(pos-1,0,val,limit&&i==up,cnt+(i==val));}else{if(i==0){ans+=dfs(pos-1,1,val,limit&&i==up,cnt);}else{ans+=dfs(pos-1,0,val,limit&&i==up,cnt+(i==val));}}}if(!limit) d[pos][lead][val][cnt]=ans;return ans;}void cal(int n,int *f){int pos=0;do{digit[pos++]=n%10;n/=10;}while(n);for(int i=0;i<=9;i++){f[i]=dfs(pos-1,1,i,1,0);}}int main(){//freopen("out.txt","w",stdout);//ios_base::sync_with_stdio(false);int l,r;memset(d,-1,sizeof(d));while(scanf("%d %d",&l,&r)&&l+r){if(l>r) swap(l,r);memset(a,0,sizeof(a));memset(b,0,sizeof(b));cal(l-1,a);cal(r,b);for(int i=0;i<=9;i++)printf("%d%c",b[i]-a[i],i==9?'\n':' ');}return 0;}




1 0