Balanced Number HDU - 3709

原题链接
考察:数位dp
这个用循环没想出来怎么写,反倒是dfs写法很容易理解(.),菜是原罪,如果有循环预处理写法请告诉本蒟蒻QAQ.
思路:
  参考数位dp记搜模板.dfs带四个变量:

LL dfs(int pos,int sum,int mid,bool limit)
/*分别是枚举到第几位,平衡点的和(以平衡点在左为正),平衡点位置,是否是边界*/

  显然平衡点位置和目前枚举的和都是需要记录的,pos和Limit都是模板不用说.
  一个小剪枝是sum<0即可返回.
  最重要的,limit==0时才能记录.否则以左分支(无限制),右分支就会返回相同的结果.
大概需要找个时间补补前导零的做法(.

Code

#include <iostream> 
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 21,M = 2010;
int a[N],cnt;
LL f[N][M][N];//对应预处理,实际上省了共i位数这一维. 
LL dfs(int pos,int sum,int mid,bool limit)
{
	if(!pos) return !sum;
	if(sum<0) return 0;
	//为什么需要判断limit,因为有限制的前提下,相同状态计数结果不同 
	if(!limit&&f[pos][sum][mid]!=-1) return f[pos][sum][mid];
	int len = limit?a[pos]:9;
	LL res = 0;
	for(int i=0;i<=len;i++)
	  res+=dfs(pos-1,sum+(pos-mid)*i,mid,limit&&(i==len));
	if(!limit) f[pos][sum][mid] = res;
	return res;
}
LL dp(LL n)
{
	if(!n) return 1;
	cnt = 0;
	while(n) a[++cnt] = n%10,n/=10;
	/*枚举平衡点*/
	LL res = 0;
	for(int i=1;i<=cnt;i++)
	  res+=dfs(cnt,0,i,1);
	return res-cnt+1;
}
int main()
{
	int T;
	scanf("%d",&T);
	memset(f,-1,sizeof f);
	while(T--) 
	{
		LL l,r;
		scanf("%lld%lld",&l,&r);
		printf("%lld\n",dp(r)-dp(l-1));
	}
	return 0;
}
posted @ 2021-05-29 20:57  acmloser  阅读(28)  评论(0编辑  收藏  举报