📅  最后修改于: 2023-12-03 15:41:36.475000             🧑  作者: Mango
在计算机科学中,数字动态规划是指一类涉及数字的问题的动态规划技术。它通常会使用递归和记忆化搜索等技术。
数字动态规划常常用于处理数字序列上的问题,如数字串匹配,计数问题,最长公共子序列等。
本文将介绍一种数字动态规划技术,即计数小于 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。