动态规划_数位DP

数位DP的一般思路

当计算一个很大的数中,符合条件的数有多少种,一般采取数位DP的方式去解决该问题。作为模板,我采用了记忆华搜索的方法。

  • 解题思路
  • 代码框架
  • 例题1 HDU 2089(简单)
  • 例题2 HDU 4507(适中)

解题思路

对于这类问题,我们将考虑数字每位上的情况。
对于数字5236,第四位上的数字,0~4可归为一种情况,5可归为一种情况,我们称5为Limited(受限制)的情况。对于前者,我们可以对次一位进行任意的排列组合(即次一位可选0~9),而后者,只能从零到次一位的最大数(对于刚才讨论的情况是0~2)。
那么,基于代码框架的角度来看,我们只需要分情况讨论即可。
由于为限制的情况我们可能会多次搜索到,所以我们采用记忆化搜索来解决重复计算的问题。


代码框架

以深度优先搜索的记忆华搜索框架:

/*
dp[len][..]表示第len位在未限制情况下的符合要求的个数。
..表示题目可能要求的条件
*/
Type dp[maxLen][];
/*
@input: len 数位
@input: isLimited 是否为受限制情况
@input: others 题目可能出现的要求
*/
Type dfs(int len, int isLimited, ..) {
    if(len == 0) return 1;
    Type ans = 0;
    if(isLimited) {
        //TODO
        //受限制情况的讨论
    }
    else {
        //TODO
        //不受情况的讨论
    }
    return isLimited?ans:dp[0][len][isSix]=ans ;//记忆化搜索
}

有时可以将一些相似的情况归并在一起计算(如例题2的代码)。

除了深度优先搜索外,我们还需要将数字的每一位进行分解,如下:

int solve(int n) {
    int len = 1;
    while(n) {
        str[len++] = n % 10;
        n /= 10;
    }
    return dfs(len-1, Limited, ..);
}

例题1 [ HDU 2089 不要62]

题目很简单,不需要过多解释,直接贴代码。

#include<cstdio>
#include<cstring>
const int maxLen = 16;
int str[maxLen];
int dp[maxLen][2];

int dfs(int len, int isLimited, int isSix) {
    if(len == 0) return 1;
    int ans = 0;
    if(isLimited) {
        for(int i=0; i<=str[len]; i++) {//0~str[len]
            if(i==4 || i==2 && isSix) continue;
            ans += dfs(len - 1, i==str[len], i==6);
        }
    }
    else {
        if(dp[len][isSix]>=0)
            return dp[len][isSix];
        ans += (8-isSix) * dfs(len - 1, 0, 0);//0 1 3 5 7 8 9 | 2
        ans += dfs(len - 1, 0, 1);//6
    }
    return isLimited?ans:dp[len][isSix]=ans ;
}

int solve(int n) {
    int len = 1;
    while(n) {
        str[len++] = n % 10;
        n /= 10;
    }
    return dfs(len-1, true, false);
}

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

例题2 [ HDU 4507 吉哥系列故事——恨7不成妻]

题目较为困难,但对于该框架来说,多想了几步而已。

TIPS
1. 考虑(A1+b) + (A2+b) + (A3+b) + ··· + (Acount+b) 与 A1 + A2 + A3 + ··· + Acount 的关系

2.考虑 (A1+b)2 + (A2+b)2 + (A3+b)2 + ··· + (Acount+b)2 与 A12 + A22 + A32 + ··· + Acount2 的关系

附AC代码

#include<cstdio>
#include<cstring>
typedef __int64 ll;
const int maxLen = 32;
const ll MOD = 1e9 + 7;
int str[maxLen];
ll powTen[maxLen];

struct node {
    ll sum;
    ll cnt;
    ll ans;
    node() { this->sum = 0;  this->cnt = -1; this->ans = 0; }
    node(ll sum, ll cnt, ll ans) { this->sum = sum; this->cnt = cnt; this->ans = ans; }
};

node dp[maxLen][10][10];

node dfs(int len, int isLimited, int sum, int mod) {
    if(len == 0) {
        return node(0,!(sum%7==0 || mod ==0),0);
    }

    if(dp[len][sum][mod].cnt>=0 && !isLimited)
            return dp[len][sum][mod];

    node x = node(0, 0, 0);
    int maxBit = isLimited?str[len]:9;

    for(int i=0; i<=maxBit; i++) {
        if(i==7) continue;
        ll base = powTen[len] * i % MOD;
        node bff = dfs(len-1, isLimited && i==maxBit, (sum+i)%7, (mod*10+i)%7);
        (x.cnt += bff.cnt) %= MOD;
        (x.sum += bff.sum + bff.cnt * base % MOD) %= MOD;
        (x.ans += bff.ans + 2 * base * bff.sum % MOD + bff.cnt * (base * base % MOD) % MOD) %= MOD;
    }
    return isLimited ? x : dp[len][sum][mod]=x;
}

ll solve(ll n) {
    int len = 1;
    while(n>0) {
        str[len++] = n % 10;
        n /= 10;
    }
    return dfs(len-1, true, 0, 0).ans;
}

int main() {

    ll n, m;
    // { init
    powTen[1] = 1;
    for(int i=2; i<maxLen; i++) {
        powTen[i] = powTen[i-1] * 10 % MOD;
    }
    // }
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%I64d%I64d", &n, &m);
        printf("%I64d\n",(solve(m) + MOD  - solve(n - 1))%MOD);
    }
    return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值