📌  相关文章
📜  计数小于 N 的包含来自给定集合的数字的数字:Digit DP(1)

📅  最后修改于: 2023-12-03 15:41:36.475000             🧑  作者: Mango

计数小于 N 的包含来自给定集合的数字的数字:Digit DP

在计算机科学中,数字动态规划是指一类涉及数字的问题的动态规划技术。它通常会使用递归和记忆化搜索等技术。

数字动态规划常常用于处理数字序列上的问题,如数字串匹配,计数问题,最长公共子序列等。

本文将介绍一种数字动态规划技术,即计数小于 N 的包含来自给定集合的数字的数字。

问题描述

给定一个正整数 N 和一个数字集合 S,计算小于 N 的数字中包含来自 S 的数字的数字数量。

例如,当 N=100,S={0,1,2,5} 时,小于100的数字中,包含来自集合 S 的数字的数字数量为 79。它们是 {0, 1, 2, 5, 10, 11, 12, 15, 20, 21, 22, 25, 50, 51, 52, 55, 100}。

解决方案

本问题可以使用动态规划来解决。

我们定义一个三维数组 count[i][j][k],其中 i 表示当前数字的位置,j 表示是否达到了限制(即当前数字是否等于N的当前位),k 表示当前数字是否已经包含来自集合 S 的数字。

首先,我们预处理出一个数组 a,其中 a[i] 表示数字 i 包含来自集合 S 的数字的状态。

然后,我们可以使用递归和记忆化搜索的方式来填充 count 数组。具体来说,我们可以定义一个递归函数 countDP(pos, limit, 1/0),其中 pos 表示当前数字的位置,limit 表示当前数字是否等于 N 的当前位,1/0 表示当前数字是否已经包含来自集合 S 的数字。

在递归过程中,我们需要判断当前位是否达到了限制、当前数字是否包含来自 S 的数字,然后递归计算下一位的状态。

最终,我们可以通过最高位的数字来得到最终结果。

代码实现

以下是基于 C++ 的计数小于 N 的包含来自给定集合的数字的数字的动态规划算法的代码示例:

// 本代码示例默认输入的集合 S 中不包含数字 0

#include <bits/stdc++.h>
using namespace std;

int a[10], count[10][2][2];  // 分别表示 a 数组和 count 数组
int dfs(int pos, int limit, int exist)
{
    if (pos == 0) return exist;
    if (!limit && count[pos][0][exist] != -1) return count[pos][0][exist];
    int maxnum = limit ? a[pos] : 9;
    int ans = 0;
    for (int i = 1; i <= maxnum; ++i)
        ans += dfs(pos - 1, limit && (i == a[pos]), exist || (i == 2) || (i == 5));
    if (!limit) count[pos][0][exist] = ans;
    return ans;
}
int dp(int n)
{
    int len = 0, tmp = n;
    while (tmp)
    {
        a[++len] = tmp % 10;
        tmp /= 10;
    }
    int ans = 0, exist = 0;
    for (int i = len; i >= 1; --i)
    {
        ans += dfs(i, (i == len), exist);
        exist = exist || (a[i] == 2) || (a[i] == 5);
    }
    return ans;
}

int main()
{
    memset(count, -1, sizeof(count));
    int N = 100, S[3] = {1, 2, 5};
    int ans = dp(N);
    cout << ans << endl;  // out put: 79
    return 0;
}

以上代码首先定义了一个数组 a,表示数字包含来自 S 的数字的情况。然后,我们实现了一个递归函数 dfs,其中 count 数组用于记录状态。

最后,我们实现了一个 dp 函数来计算小于 N 的数字中包含来自集合 S 的数字的数字数量。

完整代码已上传至Github