(数位dp)[洛谷 P4999] 烦人的数学作业

本文介绍了如何使用数位动态规划解决区间内每个数字上一位的数字和问题,通过递归和记忆化搜索优化暴力解法,避免重复计算。文章还提到考虑取模和处理减法运算中的边界问题。最后,作者提到了另一种思路——统计每个数位出现的次数以求和。
摘要由CSDN通过智能技术生成

解题思路

很明显这是一道数位dp的题目

求出一个区间内每一个数字的上每一位的数字和
首先暴力肯定是不可能的,数据上限太大了
那么我们可以借助数位dp的想法 , 既然我要求出[ l ,r ]内的数字总和,我可以求出 [ 1 , r ] [1 , r ] [1,r]的数字和减去 [ 1 , l − 1 ] [1 , l-1 ] [1,l1]的数字和即可

那么问题就转化为了求出 [ 1 , n ] [1 , n ] [1,n]内的数字和是多少

第一步肯定是把数字拆开,并初始化数组

ll solve(ll o)
{
	v.clear();
	do{
		v.push_back(o % 10);
		o /= 10;
	}while (o);
	//初始化数组
	memset(dp,-1,sizeof dp);
	return dfs(v.size() , 0 , 1);
}

接下来就是核心部分

  • pos : 目前遍历到了第几位
  • sum:到当前这一位的时候,前面几位的数字和是多少
  • limit:是否有限制,最大数是固定的n,假设n=12345,那么此时检索到第三位的时候,前面两位分别是1和0,此时第三位取0~9任意一个数字都是成立的,如果前两位是1和2,那么这一位范围就是0~3
ll dfs(int pos, int sum , bool limit)
{
	//遍历结束了,返回sum值
	if(!pos) return sum;
	//记忆化搜索
	if(~dp[pos][sum][limit]) return dp[pos][sum][limit];
	//根据limit来设置上限
	int maxn = (limit ? v[pos-1] : 9);
	ll res = 0;
	for(int i=0;i<=maxn;i++){
		
		//前进一位,sum记录一下和
		//这里说明一下a[pos-1] == i一定要结合i<=maxn来看
		res = (res + dfs(pos-1 , sum + i , limit && (i == v[pos-1])));
	}
	//记忆化搜索
	return dp[pos][sum][limit] = res;
}

细节讲解

一定不要忘了取模,仔细读题

并且涉及到减法运算,很可能会出现的问题是

a > b , 但是 (a % mod) < b

这就会导致a-b变成负数

所以解决方法就是(a + b + mod) % mod

总代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e9 + 7;
int n;
vector<int> v;
ll dp[200][400][2];
ll dfs(int pos, int sum , bool limit)
{
	//遍历结束了,返回sum值
	if(!pos) return sum;
	//记忆化搜索
	if(~dp[pos][sum][limit]) return dp[pos][sum][limit];
	//根据limit来设置上限
	int maxn = (limit ? v[pos-1] : 9);
	ll res = 0;
	for(int i=0;i<=maxn;i++){
		
		//前进一位,sum记录一下和
		//这里说明一下a[pos-1] == i一定要结合i<=maxn来看
		res = (res + dfs(pos-1 , sum + i , limit && (i == v[pos-1])));
	}
	//记忆化搜索
	return dp[pos][sum][limit] = res;
}
ll solve(ll o)
{
	v.clear();
	do{
		v.push_back(o % 10);
		o /= 10;
	}while (o);
	memset(dp,-1,sizeof dp);
	return dfs(v.size() , 0 , 1);
}
int main()
{
	cin >> n;
	long long a,b;
	while (n--){
		cin >> a >> b;
		cout << (solve(b) - solve(a-1) + N) % N << endl;
	}
	return 0;
}

思路拓展

我又想到另一个思路

完全可以算出来 [ 1 , n ] [ 1 , n ] [1,n]中的每个数字出现的个数,然后累加起来就行了
这就完全是数位dp的基础例题。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值