HDU-3709 Balanced Number(数位DP)

题意:给定一个区间[a,b],求区间内的平衡数个数,平衡数指的是存在一个支点使数形成杠杆平衡的数。(0<=a<=b<=1e18)

支点是判断一个数是否平衡的重要因素,因此把支点当参数传下来,并压在dp数组中。每枚举一个数就算一次这个数乘力矩的值,累计在sum中,最后判断sum是否为0即可。

但是这种方法存在一个问题,零的支点可以放在每个位置,所以每枚举一个支点,零都被算了一次,所以返回值应减去(p-1),p为数的位数。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
LL dp[20][20][2803];
int num[23];

LL dfs(int k,int sum,int &torque,bool ismax)
{
	if(k==0)return sum==1400;
	if(!ismax && ~dp[k][torque][sum])return dp[k][torque][sum];
	int maxer=ismax?num[k]:9;
	LL res=0;
	FOR(i,0,maxer)
		res+=dfs(k-1,sum+(k-torque)*i,torque,ismax&&i==maxer);
	if(!ismax)dp[k][torque][sum]=res;
	return res;
}
LL solve(LL x)
{
	int p=0;   //如果x是零时恰好返回1,所以输入时不需要特判
	while(x)
	{
		num[++p]=x%10;
		x/=10;
	}
	LL res=0;
	FOR(i,1,p)res+=dfs(p,1400,i,1);
	return res-p+1;   //特判0
}

int main()
{
	int T;
	scanf("%d",&T);
	memset(dp,-1,sizeof(dp));
	while(T--)
	{
		LL A,B;
		scanf("%lld%lld",&A,&B);
		printf("%lld\n",solve(B)-solve(A-1));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/80380563