http://acm.hdu.edu.cn/showproblem.php?pid=3709
这题也是个数位DP,我的数位DP都是写成的记忆化搜索的形式,个人觉得这样要好理解点。
这题与其他数位DP的差别就是要枚举每一个点作为支点的情况,然后最后还要减掉重复计算的为0的情况。
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define int64 __int64
#define ll long long
#define mod 1000000007
#define M 20
int num[20];
int64 x , y , dp[20][20][2000];
int64 Dfs(int index , int mid , int sum , int limit)//mid是支点,sum是前面数字的力矩和
{
if (!index)
{
if (sum == 0)return 1;
return 0;
}
if (!limit && dp[index][mid][sum] != -1)return dp[index][mid][sum];
int i , up =limit ? num[index] : 9;
int64 ret = 0;
for (i = 0 ; i <= up ; i++)
{
int tsum = sum+(index-mid)*i;
if (tsum >= 0)//剪枝
ret += Dfs(index-1 , mid , tsum , limit&&i==up);
}
if (!limit)dp[index][mid][sum] = ret;
return ret;
}
int64 Solve(int64 k)
{
if (k < 0)return 0;
int len = 0;
int64 ret = 0;
while (k)
{
num[++len] = k%10;
k /= 10;
}
for (int i = len ; i > 0 ; i--)//枚举支点
{
memset(dp , -1 , sizeof dp);
ret += Dfs(len , i , 0 , 1);
}
return ret-len+1;//这里减掉重复计算0的情况
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
scanf("%I64d%I64d",&x,&y);
printf("%I64d\n",Solve(y)-Solve(x-1));
}
return 0;
}