数位DP经典例题
例题1:度的数量
题意
求给定区间中满足下列条件的整数个数:这个数恰好等于个互不相等的的整数次幂之和。
例如,设,则有且仅有下列三个数满足题意:
题目链接:https://www.acwing.com/problem/content/1083/
数据范围
思路
首先,按照数位DP惯用套路,将问题转化为,其中表示前个数中满足要求的数字个数。
然后,我们可以将转化为进制,这样通过统计的个数就能快速判断是否满足要求。
令表示如果正在枚举第位且已经有个,答案有多少个(有limit的单算)。则
由于记忆化搜索版数位DP时间复杂度较高,因此考虑剪枝:在枚举的过程中,如果数量超过了,则不继续搜索。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 35;
int l, r, k, b;
int f[N][N];
//f(i, j)表示如果正在枚举第i位且已经有j个b的整数次幂,答案有多少个
//最终答案为f(p - 1, 0)
int a[N];
int dfs(int p, int num, bool limit) //limit为1表示当前位上有限制,为0表示无限制
{
if(p < 0) return num == k; //枚举完最后一位,判断是否为答案
if(!limit && f[p][num] != -1) return f[p][num];
int up = limit ? min(a[p], 1) : 1; //当前位的选择上限
int ans = 0;
for(int i = 0; i <= up; i ++) {
if(num + i > k) continue;
ans += dfs(p - 1, num + i, limit && i == a[p]);
}
if(!limit) f[p][num] = ans;
return ans;
}
int solve(int x)
{
int p = 0;
while(x) {
a[p ++] = x % b;
x /= b;
}
memset(f, -1, sizeof f);
return dfs(p - 1, 0, 1);
}
int main()
{
cin >> l >> r >> k >> b;
printf("%d\n", solve(r) - solve(l - 1));
return 0;
}
例题2:数字游戏
题意
某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如。
现在大家决定玩一个游戏,指定一个整数闭区间,问这个区间内有多少个不降数。
题目链接:https://www.acwing.com/problem/content/1084/
数据范围
思路
在搜索的时候记录一下上一位是多少即可。
令表示如果正在枚举第位且上一位是,答案有多少个(有limit的单算)。则
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15;
int l, r;
int f[N][N];
int a[N];
int dfs(int p, int pre, bool limit)
{
if(p < 0) return 1;
if(!limit && f[p][pre] != -1) return f[p][pre];
int up = limit ? a[p] : 9;
int ans = 0;
for(int i = pre; i <= up; i ++) {
ans += dfs(p - 1, i, limit && i == a[p]);
}
if(!limit) f[p][pre] = ans;
return ans;
}
int solve(int x)
{
int p = 0;
while(x) {
a[p ++] = x % 10;
x /= 10;
}
memset(f, -1, sizeof f);
return dfs(p - 1, 0, 1);
}
int main()
{
while(~scanf("%d%d", &l, &r)) printf("%d\n", solve(r) - solve(l - 1));
return 0;
}
例题3:Windy数
题意
Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为的正整数被称为 Windy 数。
Windy 想知道,在和之间,包括和,总共有多少个 Windy 数?
题目链接:https://www.acwing.com/problem/content/1085/
数据范围
思路
这道题需要考虑前导零。考虑前导零的意思就是,前导零部分不需要满足题目中的限制条件。
与limit处理方法类似,是否是前导零用一个标记lead表示,然后单独去讨论前导零的情况即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 15;
char L[N], R[N];
int a[N];
ll f[N][N];
ll dfs(int p, int pre, bool lead, bool limit)
{
if(p < 0) return 1;
if(!limit && !lead && f[p][pre] != -1) return f[p][pre];
int up = limit ? a[p] : 9;
ll ans = 0;
for(int i = 0; i <= up; i ++) {
if(lead) { //如果为前导零
if(!i) ans += dfs(p - 1, 0, 1, limit && i == a[p]); //如果当前填0
else ans += dfs(p - 1, i, 0, limit && i == a[p]); //如果当前不填0
}
else { //不是前导零
if(abs(i - pre) >= 2) ans += dfs(p - 1, i, 0, limit && i == a[p]);
}
}
if(!limit && !lead) f[p][pre] = ans;
return ans;
}
ll solve(string s)
{
int p = 0;
int len = s.size();
for(int i = len - 1; i >= 0; i --) a[p ++] = s[i] - '0';
memset(f, -1, sizeof f);
return dfs(p - 1, 0, 1, 1);
}
bool check(string s)
{
int len = s.size();
for(int i = 1; i < len; i ++) {
int a = s[i] - '0', b = s[i - 1] - '0';
if(abs(a - b) < 2) return false;
}
return true;
}
int main()
{
cin >> L >> R;
printf("%lld", solve(R) - solve(L) + check(L));
return 0;
}
例题4:数字游戏II
题意
某人又命名了一种取模数,这种数字必须满足各位数字之和为。
现在大家又要玩游戏了,指定一个整数闭区间,问这个区间内有多少个取模数。
题目链接:https://www.acwing.com/problem/content/1086/
数据范围
思路
这道题就是就是经典的计数DP拓展到了数位DP。
令表示当前枚举到第位且模数为的情况下,方案数为多少。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15, M = 110;
int l, r, n;
int a[N];
int f[N][M];
int dfs(int p, int num, bool limit)
{
if(p < 0) return !num;
if(!limit && f[p][num] != -1) return f[p][num];
int up = limit ? a[p] : 9;
int ans = 0;
for(int i = 0; i <= up; i ++) {
ans += dfs(p - 1, (num + i) % n, limit && i == a[p]);
}
if(!limit) f[p][num] = ans;
return ans;
}
int solve(int x)
{
int p = 0;
while(x) {
a[p ++] = x % 10;
x /= 10;
}
memset(f, -1, sizeof f);
return dfs(p - 1, 0, 1);
}
int main()
{
while(cin >> l >> r >> n) printf("%d\n", solve(r) - solve(l - 1));
return 0;
}
例题5:不要62
题意
问之间有多少数字里面没有和。
题目链接:https://www.acwing.com/problem/content/1087/
数据范围
思路
首先看没有的情况,我们只需要在枚举当前位时,把去掉即可。
对于没有的情况,我们记录一个pre,如果pre为且当前位为,则continue
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15;
int l, r;
int a[N];
int f[N][N];
int dfs(int p, int pre, bool limit)
{
if(p < 0) return 1;
if(!limit && f[p][pre] != -1) return f[p][pre];
int up = limit ? a[p] : 9;
int ans = 0;
for(int i = 0; i <= up; i ++) {
if(i == 4) continue;
if(pre == 6 && i == 2) continue;
ans += dfs(p - 1, i, limit && i == a[p]);
}
if(!limit) f[p][pre] = ans;
return ans;
}
int solve(int x)
{
int p = 0;
while(x) {
a[p ++] = x % 10;
x /= 10;
}
memset(f, -1, sizeof f);
return dfs(p - 1, 0, 1);
}
int main()
{
while(cin >> l >> r) {
if(!l && !r) break;
cout << solve(r) - solve(l - 1) << endl;
}
return 0;
}
例题6:计数问题
题意
给定两个整数和,求和之间的所有数字中的出现次数。
题目链接:https://www.acwing.com/problem/content/340/
数据范围
思路
这道题需要统计个量,因此我们可以跑次数位DP,分别统计每个数字的出现个数。
由于这道题前导零是不算在内的,因此需要考虑前导零的情况。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15;
int l, r;
int a[N];
int f[N][N];
int dfs(int p, int num, int x, bool lead, bool limit)
{
if(p < 0) return num;
if(!limit && !lead && f[p][num] != -1) return f[p][num];
int up = limit ? a[p] : 9;
int ans = 0;
for(int i = 0; i <= up; i ++) {
if(lead) {
if(!i) ans += dfs(p - 1, num, x, lead, limit && i == a[p]);
else ans += dfs(p - 1, num + (x == i), x, 0, limit && i == a[p]);
}
else ans += dfs(p - 1, num + (x == i), x, lead, limit && i == a[p]);
}
if(!limit && !lead) f[p][num] = ans;
return ans;
}
int solve(int x, int t)
{
int p = 0;
while(x) {
a[p ++] = x % 10;
x /= 10;
}
memset(f, -1, sizeof f);
return dfs(p - 1, 0, t, 1, 1);
}
int main()
{
while(cin >> l >> r) {
if(!l && !r) continue;
if(l > r) swap(l, r);
for(int i = 0; i < 10; i ++) {
printf("%d ", solve(r, i) - solve(l - 1, i));
}
printf("\n");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)