数位dp入门 -- Balanced Number HDU - 3709

Balanced Number HDU - 3709

题意:
给你T次询问,每次询问给你两个数l, r(0 <= l <= r <= 1e18),问你l,r间有多少个平衡数,平衡数定义:平衡的数字必须与某些数字的支点保持平衡。例如,4139是一个枢轴固定为3的平衡数。左侧部分和右侧部分的扭矩分别为4 * 2 + 1 * 1 = 9和9 * 1 = 9。

思路:

  • dp[pos][sum], 一维表示一个数从左到右第pos位,二维sum表示支点左右相加后的平衡值,支点左边的数设为正,右边设为负,最后sum = 0则是平衡数(当sum < 0时显然就可以剪枝了)。
  • 一个数有cot位,遍历每一位支点dp一次,把贡献相加,但要注意每换个支点dp,支点为0,支点左右全为0的情况就多算了一次,所以最后贡献和要减去cot。(如5-999,有三种情况0,00,000,都代表0,减去2)

code:


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
long long dp[30][3005];
int head[30];

long long dfs(int pos, int bal, int sum, bool limit) { //pos表示数位,bal表示支点,sum表示平衡值
	if(pos == -1) {
		if(sum == 0) return 1;
		else return 0; 
	}
	if(sum < 0) return 0;  //sum < 0已不可能是一个平衡数,剪枝
	if(!limit && dp[pos][sum] != -1) return dp[pos][sum];
	
	int max_digit = limit ? head[pos] : 9;
	long long ans = 0;
	for(int i = 0; i <= max_digit; i++){
		
		ans += dfs(pos - 1, bal, sum + (pos - bal) * i, limit && i == max_digit);  //pos - bal
	}
	if(!limit) dp[pos][sum] = ans;
	return ans;
}

long long solve(long long l){
	if(l < 0) return 0;
	int cot = 0;
	while(l){
		head[cot++] = l % 10;
		l /= 10;
	}
	
	long long ans = 0;
	for(int i = 0; i <= cot - 1; i++){
		memset(dp, -1, sizeof(dp));
		ans += dfs(cot - 1, i, 0, true);
	}
	return ans - cot + 1;  //减去全零的情况有几位有几次重复的,最终只需保留一种,
}
int main(){
	int T;
	long long l, r;
	scanf("%d", &T);
	while(T--){
		scanf("%lld%lld", &l, &r);
		printf("%lld\n", solve(r) - solve(l - 1)); 
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值