题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3709
记忆化 ,用的数位dp的模式感觉 ,数位dp也是菜开始学的 感觉很难看了一些博客 最终艰难的把它a了,写个题解来巩固一下把尽量写得详细点。
网上很多博客都说直接枚举支点 其实对这个不是很熟,下面就用我的话来表述一下这个题吧。
首先是暴力dfs,对于指定长度的一个数,小于它的数有多少是平横数,根据自己理解到的数位dp的东西,根据指定长度,那么对于长度达不到的数前面补0,这样就避免了位数的关系 而且这样也可以保证每一位数都能枚举到,那么这个题 我们枚举那个数位是平衡点 假设是第i位是平衡点 那么只需要重左加到右每次加的时候乘上位置差就可以了。
然后对于dp记忆 dp[i][j][sum] 表示剩余长度为i的时候 平衡点为就的时候 两边边差为sum的时候有多少种平衡情况 注意havemax标记 因为如果前一位不是最大值得时候那么当前这一为可以随便取0~9 否则就只能取到当前位的最大值。注意dp记录的也只能是没有最大值得情况 因为能对于每一种情况 如果没有最大值 那么枚举的后续都是一样的。ok上代码
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define LL long long
LL dp[20][20][2050];
int dig[20];
LL dfs(int len,int pos,int sum,bool haveMax)
{
if(len<0)
return sum==0?1:0;
if(!haveMax && dp[len][pos][sum]!=-1)
return dp[len][pos][sum];
int maxNum = haveMax?dig[len]:9;
LL ans = 0;
for(int i = 0;i<=maxNum;i++)
{
ans+=dfs(len-1,pos,sum+(len-pos)*i,haveMax&&i==maxNum);
}
if(!haveMax)
{
dp[len][pos][sum] = ans;
}
return ans;
}
LL solve(LL n)
{
int len = 0;
while(n)
{
dig[len++] = n%10;
n/=10;
}
LL ans = 0;
for(int i = 0;i<len;i++)
{
ans+=dfs(len-1,i,0,true);
//printf("%lld\n",ans);
}
return ans-(len-1);
}
int main()
{
int t;
memset(dp,-1,sizeof(dp));
cin >> t;
while(t--)
{
LL l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}