码迷,mamicode.com
首页 > 其他好文 > 详细

【一天一DP计划】数位dp

时间:2019-10-08 12:50:59      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:记录   res   lse   数字游戏   blog   work   html   com   ongl   

入坑之好博

一本通 数位DP

浅谈数位DP

## P4127 同类分布

现在关键的问题是:怎样记录dp状态?

这里 st可达到 1e18 显然是不能作为dp转移的下标直接记录的

所以我们考虑取模

我们最理想的模数当然是把每次搜到最后得到的数字各个位数之和

但是我们发现在这个过程中 sum是发生变化的

所以我们就应该以一个定值作为模数

那好,我们虽然不知道最后各位之和的结果,我们枚举总可以吧

我们只需要枚举所有的各位数字之和作为模数

最后判断 sum 和枚举的 mod相等并且 st%sum=0 的数就是符合题意的答案

/*
reference:
    https://www.luogu.org/blog/virus2017/p4127#
date:
    2019.10.03
solution:
    - 既然st的范围是1e18那么long long都存不下肯定不能记为dp的状态
        - 那么肯定要取模!模数为什么呢 ?最好为当前模数和sum
            - 可是sum在转移的时候一直在改变,那么之前记录的dp状态就无效了!
                - 那我们换个思路,模数呢最大为18*9,那我们枚举每个模数,判断当前在这个模数意义下是否合法
                    - 合法的条件:sum==mod && st==0(st要一直%mod) 
*/
int a,b,len,mod; 
int dp[18+5][18*9+5][18*9+5],bit[18+5];

inline int dfs(int pos,int sum,int st,int limit){
    if(pos>len && sum==0)return 0; 
    if(pos>len)return sum==mod && st==0;
    if(!limit && dp[pos][sum][st]!=-1)return dp[pos][sum][st];
    int high=limit?bit[len-pos+1]:9;
    int res=0;
    rep(i,0,high){
        res+=dfs(pos+1,sum+i,(10ll*st+i)%mod,i==high && limit);
    }
//  return limit?res:dp[pos][sum][st]=res;
    if(!limit)dp[pos][sum][st]=res;
    return res;
}

inline int work(int x){
    len=0;
    while(x){
        bit[++len]=x%10;
        x/=10;
    }
    int res=0;
    for(mod=1;mod<=len*9;++mod){
        mem(dp,-1);
        res+=dfs(1,0,0,1);
    }
    return res;
}

#undef int
int main(){
#define int long long
    freopen("tonglei.txt","r",stdin);
    rd(a),rd(b);
    printf("%lld",work(b)-work(a-1)); 
    return 0;
}

##windy数

/*
reference:
    https://www.luogu.org/blog/virus2017/solution-p2657
date:
    2019.10.03
solution:
    最妙:考虑前导零的时候,第一位的时候,0和1是没办法取到的,因为abs(i-pre)<2了,
    那么,就等价于,pre设为-2问题解决 
*/
int a,b,len;
int bit[15],dp[15][15];

inline int dfs(int pos,int pre,int lead,int limit){
    if(pos>len)return 1;
    if(!limit && dp[pos][pre]!=-1)return dp[pos][pre];
    int high=limit?bit[len-pos+1]:9;
    int res=0;
    rep(i,0,high){
        if(abs(i-pre)<2)continue;
        else if(lead && i==0)
            res+=dfs(pos+1,-2,1,i==high && limit);
        else
            res+=dfs(pos+1,i,0,i==high && limit);
    }
    if(!limit && !lead)dp[pos][pre]=res;
    return res;
}

inline int work(int x){
    len=0;
    while(x){
        bit[++len]=x%10;
        x/=10;
    }
    mem(dp,-1);
    return dfs(1,-2,1,1);
}

#undef int
int main(){
#define int long long
    #ifdef WIN64
    freopen("windy.txt","r",stdin);
    #endif
    rd(a),rd(b);
    printf("%lld\n",work(b)-work(a-1));
    return 0;
}

##数字计数(求不降数)

/*
int a,b,len,mod;
int bit[15],dp[15][15];

inline int dfs(int pos,int pre,int limit){
    if(pos>len)return 1;
    if(dp[pos][pre]!=-1 && !limit)return dp[pos][pre];
    int high=limit?bit[len-pos+1]:9;
    int res=0;
    rep(i,pre,high){
        res+=dfs(pos+1,i,i==high && limit);
    }
    if(!limit)dp[pos][pre]=res;
    return res;
}

inline int work(int x){
    len=0;
    while(x){
        bit[++len]=x%10;
        x/=10;
    }
    mem(dp,-1);
    return dfs(1,0,1);
} 
#undef int
int main(){
#define int long long
    #ifdef WIN64
    freopen("shuzi2.txt","r",stdin);
    #endif
    while(~scanf("%lld%lld",&a,&b)){
        printf("%lld\n",work(b)-work(a-1));
    }
    return 0;
}

##不要62

/*
int a,b,len;
int bit[10],dp[10][15];

inline int dfs(int pos,int pre,int limit){
    if(pos>len)return 1;
    if(dp[pos][pre]!=-1 && !limit)return dp[pos][pre];
    int high=limit?bit[len-pos+1]:9;
    int res=0;
    rep(i,0,high){
        if(i==4)continue;
        if(pre==6 && i==2)continue;
        res+=dfs(pos+1,i,i==high && limit);
    }
    if(!limit)dp[pos][pre]=res;
    return res;
}

inline int work(int x){
    mem(bit,0);
    len=0;
    while(x){
        bit[++len]=x%10;
        x/=10;
    }
    mem(dp,-1);
    return dfs(1,0,1);
}

#undef int
int main(){
#define int long long
    #ifdef WIN64
    freopen("62.txt","r",stdin);
    #endif
    while(1){
        rd(a),rd(b);
        if(a==0 && b==0)break; 
        printf("%lld\n",work(b)-work(a-1));
    }
    return 0;
}

##数字游戏(取模)

int a,b,len,mod;
int bit[15],dp[15][900];

inline int dfs(int pos,int sum,int limit){
    if(pos>len)return sum==0?1:0;
    if(!limit && dp[pos][sum]!=-1)return dp[pos][sum];
    int high=limit?bit[len-pos+1]:9;
    int res=0;
    rep(i,0,high){
        res+=dfs(pos+1,(sum+i)%mod,i==high && limit);
    }
    if(!limit)dp[pos][sum]=res;
    return res;
}

inline int work(int x){
    len=0;
    while(x){
        bit[++len]=x%10;
        x/=10;
    }
    mem(dp,-1);
    return dfs(1,0,1);
}

#undef int
int main(){
#define int long long
    #ifdef WIN64
    freopen("shuzi.txt","r",stdin);
    #endif
    while(~scanf("%lld%lld%lld",&a,&b,&mod)){
        printf("%lld\n",work(b)-work(a-1));
    }
    return 0;
}

【一天一DP计划】数位dp

标签:记录   res   lse   数字游戏   blog   work   html   com   ongl   

原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11634654.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!