Sweety

Practice makes perfect

导航

数位DP整理总结

Posted on 2017-04-27 16:22  蓝空  阅读(228)  评论(0编辑  收藏  举报
 数位动态规划是求解一个大区间[L, R]中间满足条件Q的所有数字的个数(或者和,或其他)的一种方法。它通过分析每一位上的数字,一般用 dp[len][digit][...] 来表示状态“len位长的数字,最高位数字为digit所具有的xx特性”,利用记忆化搜索保存中间结果,从而加快求解速度。 

通过求 f(n) 从0到n中满足条件Q的数字的个数,则所求的结果为 f(R) - f(L-1). 

参考于:here

大多数数位dp都可以用一个DFS函数来进行记忆化搜索:

#include <bits/stdc++.h>
using namespace std;
#define BITNUM 10
#define MAXN 10
int dp[BITNUM][MAXN];
int bits[BITNUM];

///pos为当前位,digit标记状态的值(注意压缩抽象),limit 表示digit是否是第len位(从低位向高位数,个位为第1位)的范围边界
int dfs(int pos, int digit, bool end_flag)
{
    if(!end_flag  && dp[pos][digit] != -1)///记忆化搜索,如果之前已经求出来了,则返回。注意这里要求 end_flag为false
        return dp[pos][digit];
    if(pos==0) return dp[pos][digit]=1;

    int end = end_flag ? bits[pos-1] : 9 ;///如果当前位是边界数字N对应位的最大值,则下一位的范围只能从0到边界数字N的下一位的最大值。否则为0 到 9
    int ans = 0;
    for(int i = 0; i <= end; i++)
    {
        if(!(digit==6&&i==2) && i!=4)
            ans += dfs(pos - 1, i, end_flag && (i==end));
    }

    if (!end_flag) ///digit不是第len位的最高范围,则可以将结果缓存
        dp[pos][digit] = ans;
    return ans;
}


int solve(int  x)
{
    memset(bits,0,sizeof bits);
    int pos=0;
    while(x)
    {
        bits[pos++]=x%10;
        x/=10;
    }
    return dfs(pos, bits[pos], 1);///为方便当前为设置为0
}

int main()
{
    memset(dp,-1,sizeof dp);
    int m,n;
    while(~scanf("%d%d",&m,&n)&&(m||n))
    {
       int x1=solve(m-1);
       int x2=solve(n);
        printf("%d\n",x2-x1);
    }
    return 0;
}