参考:http://www.cppblog.com/Yuan/archive/2013/03/09/139299.html#198320
用这种方法写,一个流程是,列出式子(pre*10^pos + next) pre是确定的,next是变量
所以参数就为pre,pos加上一些其他的,还有一个标记doing,表示是计算有上界限制的还是没有上界限制(所有情况,即end=9)
dfs(pos-1 , npre , doing && i ==end)
平衡,即∑a[i]*(i-o) = 0 o为支点
对于一个数,支点o是唯一的,所以不会有重复计数的情况(但有一种特殊的,就是0,00,000等都是一样的,会计算多次,
最后减去即可)
假设检查到pos处,对于上面的式子∑a[i]*(i-o) = 0,这里确定了支点为o
之前的数其∑a[i]*(i-o)的结果为pre
所以参数需要为pos , o , pre
当检查到pos=-1时,return pre == 0
否则,看当前是计算所有情况还是具体情况(doing为1表示所有情况,为0表示特殊情况,就是有个位置受到限制)
如果是所有情况且dp值!=-1,直接return
否则就枚举0到end
而支点o需要在最外层枚举出来
记忆搜索: 用这种方法写,一个流程是,列出式子(pre*10^pos + next) pre是确定的,next是变量
所以参数就为pre,pos加上一些其他的,还有一个标记doing,表示是计算有上界限制的还是没有上界限制(所有情况,即end=9)
dfs(pos-1 , npre , doing && i ==end)
ZJU AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long dp[19][19][2000];
int digit[19];
long long dfs(int pos , int o , int pre , bool doing)
{
if(pos == -1)
return pre == 0; //pos等于-1 说明已经计算到个位。此时pre就是其值,等于说明是个平衡数
if(pre < 0)
return 0; //剪枝
if(!doing && dp[pos][o][pre] != -1)
return dp[pos][o][pre];
long long ans = 0;
int end = doing ? digit[pos] : 9;
for(int i = 0 ; i <= end ; i ++)
{
int npre = pre;
npre += (pos-o)*i;
ans += dfs(pos-1 , o , npre , doing && i == end); //每位的最高位处doing为false,导致其衍生的都为false
}
if(!doing)
dp[pos][o][pre] = ans;
return ans;
}
long long cal(long long x)
{
int pos = 0;
while(x)
{
digit[pos++] = x % 10;
x /= 10;
}
long long ans = 0;
for(int o = 0 ; o < pos ; o ++)
{
ans += dfs(pos-1 , o , 0 , 1);
}
return ans - (pos-1);//duplicate 0
}
int main()
{
int T;
for(scanf("%d",&T) ; T--; )
{
long long left , right;
memset(dp,-1,sizeof(dp));
scanf("%lld%lld",&left , &right);
printf("%lld\n",cal(right) - cal(left - 1));
}
return 0;
}
到hdu上提交,半天都是wa, 逐字逐句检查,检查边界,搞了半天。最后奇葩的是,需要将%ldd 改成 %I64d !!! 改完了之后,在hdu上ac, 在zju上开始wa了。。
hdu ac 代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long dp[19][19][2000];
int digit[19];
long long dfs(int pos , int o , int pre , bool doing)
{
if(pos == -1)
return pre == 0; //pos等于-1 说明已经计算到个位。此时pre就是其值,等于说明是个平衡数
if(pre < 0)
return 0;
if(!doing && dp[pos][o][pre] != -1)
return dp[pos][o][pre];
long long ans = 0;
int end = doing ? digit[pos] : 9;
for(int i = 0 ; i <= end ; i ++)
{
int npre = pre;
npre += (pos-o)*i;
ans += dfs(pos-1 , o , npre , doing && i == end); //每位的最高位处doing为false,导致其衍生的都为false
}
if(!doing)
dp[pos][o][pre] = ans;
return ans;
}
long long cal(long long x)
{
int pos = 0;
while(x)
{
digit[pos++] = x % 10;
x /= 10;
}
long long ans = 0;
for(int o = 0 ; o < pos ; o ++)
{
ans += dfs(pos-1 , o , 0 , 1);
}
return ans - (pos-1);//duplicate 0
}
int main()
{
int T;
for(scanf("%d",&T) ; T--; )
{
memset(dp,-1,sizeof(dp));
long long left , right;
scanf("%I64d%I64d",&left , &right);
printf("%I64d\n",cal(right) - cal(left - 1));
}
return 0;
}