一天一DP计划数位dp

Posted sjsjsj-minus-si

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一天一DP计划数位dp相关的知识,希望对你有一定的参考价值。

入坑之好博

一本通 数位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的主要内容,如果未能解决你的问题,请参考以下文章

数位dp

夜深人静写算法(二十九)- 数位DP

数位DP入门

数位DP入门

数位DP小结

数位DP