Balanced Number
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 4941 Accepted Submission(s): 2345
to calculate the number of balanced numbers in a given range [x, y].
2 0 9 7604 24324
10 897
之前做题的惯性吧,做这一题的时候,感觉很麻烦。。。dfs里要枚举支点的位置,还要枚举移动的i,所以有点懵,后来知道了,支点是一定的,所以枚举所有支点的位置,dfs里的len指的是当前支点的枚举位置,那么他们之间的距离就是len-pos了,再乘以这一个点所有可能的数值,然后这个dfs再深入下去继续找,这里注意,如果sum<0直接返回0就行,一方面是数组下表不能负数,另一方面一开始sum是正的,如果能达到条件最后一定是0,是负的说明不可能
题意:
对于某个 number,你可以 fix a pivot 在某位,然后如果分成的左右两部分的 sigma(d[i] * | i - fixloc |)相等,则它是 Balanced Number。
统计区间 [a,b] 中Balance Number 的个数。
解题思路:
首先要分析出,对于某个非 0 的 number,最多可能有一个 pivot 的位置。
证明:如果有两个这样的位置,将左边位置移动到右边时,左边的 sigma 一定增大,右边的 sigma 最多保证不减,不可能增大,故不可能再次相等。
于是可以枚举这样的位置,然后分类统计求和。
由于 0 对于每个位置都会被统计到,最后要再减去重复的。
Ps:在 数位dp 的 dfs 中,对于 0 的看待,是 len 个0 放一起,以后这个细节要注意。
分析:
还是根据数的性质保存状态,显然数的性质涉及到力矩,支点的位置
所以dp[i][j][k] :
i : 正在处理的数位
j : 支点的位置
k : 左右力矩之和(正负算,为 0 就是平衡的)
dp[i][j][k]就是具有上述性质的数的个数
dp[i][j][k] = dp[i-1][j][ k + dig*(i-j)]
其中 dig 为数位 i 处枚举的可能的数字
状态转移中j没有转移?支点的位置需要另外枚举
枚举支点位置?会不会出现算某个位置的时候算了一遍数字x,算另一个位置的时候又算了一遍x这样算重复的情况?
想一下,一个数如果是平衡数,那么它的支点位置必然是固定的(0除外)
所以不会算重复,只有最后把重复的0减去就好,0算了len遍,故重复的0有(len-1)个
这里提一个事,这题的dp[i][j][k]保存了3个维度的信息,但是实际上,它在状态转移的时候第二维的j并没有改变
看代码里面的dfs的过程也是,传入的一个参数p从来都没有变过,为什么不省去这一维呢?
首先,dfs的时候确实可以不传参数p,直接让其在全局里面,然后枚举位置的时候改变其值,每次递归的时候可以直接用
但是dp还是应该保存这一维,因为题目是多组数据,秉着算过就记录下来的原理,可以为后面更多组数节约时间
前面B题里面也有一维没有参与状态转移,但是依旧保存下来了是同样的原理(B题的dfs没有传那个不变的参数K)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
ll dp[20][20][10000];
int bit[20];
ll dfs(int len, int pos, int sum, int limit)
{
if(len < 1) return sum == 0;
if(sum < 0) return 0;
if(!limit && dp[len][pos][sum] != -1) return dp[len][pos][sum];
int last = limit ? bit[len] : 9;
ll ans = 0;
for(int i = 0; i <= last; i++)
{
ans += dfs(len-1, pos, sum+(len-pos)*i, limit && i == last);
}
if(!limit) dp[len][pos][sum] = ans;
return ans;
}
ll cal(ll n)
{
int k = 0;
while(n)
{
bit[++k] = n%10;
n /= 10;
}
ll ans = 0;
for(int i = k; i >= 1; i--)
{
ans += dfs(k, i, 0, 1);
}
return ans-k+1; //每个位置都走了一遍0
}
int main()
{
int t;
memset(dp, -1, sizeof(dp));
scanf("%d", &t);
while(t--)
{
ll a, b;
scanf("%I64d%I64d", &a, &b);
printf("%I64d\n", cal(b) - cal(a-1));
}
return 0;
}