HDU3709 Balanced Number —— 数位DP

题目链接:https://vjudge.net/problem/HDU-3709

 

Balanced Number

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 6615    Accepted Submission(s): 3174


Problem Description
A balanced number is a non-negative integer that can be balanced if a pivot is placed at some digit. More specifically, imagine each digit as a box with weight indicated by the digit. When a pivot is placed at some digit of the number, the distance from a digit to the pivot is the offset between it and the pivot. Then the torques of left part and right part can be calculated. It is balanced if they are the same. A balanced number must be balanced with the pivot at some of its digits. For example, 4139 is a balanced number with pivot fixed at 3. The torqueses are 4*2 + 1*1 = 9 and 9*1 = 9, for left part and right part, respectively. It's your job
to calculate the number of balanced numbers in a given range [x, y].
 

 

Input
The input contains multiple test cases. The first line is the total number of cases T (0 < T ≤ 30). For each case, there are two integers separated by a space in a line, x and y. (0 ≤ x ≤ y ≤ 1018).
 

 

Output
For each case, print the number of balanced numbers in the range [x, y] in a line.
 

 

Sample Input
2 0 9 7604 24324
 

 

Sample Output
10 897
 

 

Author
GAO, Yuan
 

 

Source

 

 

题解:

1.对于一个数,我们枚举每个位置作为平衡点,去判断是否存在一个位置使得这个数左右平衡。对于非0的数,如果它是平衡数,那么它的平衡点只有一个。(因为假设aaaa0bbb这个数的平衡点0,如果把平衡点往左移,那么左边变轻,右边变重,原本是平衡的,那改变之后就往右倾斜了。同理平衡点往右移。)所以是+1,正好一一对应。但是对于0,它的任意一个位置都可以作为平衡点,所以需要去重。

2. dp[pos][pivot][num]:处理到pos位置,以pivot为平衡点,且左力矩比右力矩大num的情况下,有多少种情况。

问:

1.为什么dp数组要有“pivot”这一维,直接:dp[pos][num]不行吗?

答:虽然有num记录了当前位的“左力矩 - 右力矩”的大小,但是如果两种情况的pivot不同,那么在pos后面的位置,他们的值的变化是不一样的,即这两种情况不是等价的。只有在相同的位置pos下,他们的平衡点pivot相同,且“左力矩 - 右力矩”的大小且相同,后面位置的数字变化才是等价的。

2.dp[pos][leftnum][rightnum] 这样记录不行吗?

答:其实这样跟dp[pos][num]的情况是一样的,都没有记录平衡点pivot的位置,导致后面位置的数值变化可能不等价。

 

 

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <string>
 6 #include <vector>
 7 #include <map>
 8 #include <set>
 9 #include <queue>
10 #include <sstream>
11 #include <algorithm>
12 using namespace std;
13 typedef long long LL;
14 const double eps = 1e-6;
15 const int INF = 2e9;
16 const LL LNF = 9e18;
17 const int MOD = 1e9+7;
18 const int MAXN = 1e6+10;
19 
20 int digit[20];
21 LL dp[20][20][4000];
22 
23 LL dfs(int pos, int pivot, int num, bool lim)
24 {
25     if(!pos) return (num==0);
26     //剪枝,因为先加左边的,后减左边的,最终结果需要为0,如果过程中都出现负数,那减下去更加负,所以不可能符合条件
27     if(num<0) return 0;
28     if(!lim && dp[pos][pivot][num]!=-1) return dp[pos][pivot][num];
29 
30     LL ret = 0;
31     int maxx = lim?digit[pos]:9;
32     for(int i = 0; i<=maxx; i++)    //num+(pos-pivot)*i: 高位的加, 低位的减。这条式子很漂亮,刚好能满足力矩的计算
33         ret += dfs(pos-1, pivot, num+(pos-pivot)*i, lim&&(i==maxx));
34 
35     if(!lim) dp[pos][pivot][num] = ret;
36     return ret;
37 }
38 
39 LL solve(LL n)
40 {
41     int len = 0;
42     while(n)
43     {
44         digit[++len] = n%10;
45         n /= 10;
46     }
47 
48     LL ret = 0;
49     for(int i = 1; i<=len; i++)
50         ret += dfs(len, i, 0, true);
51 
52     /*
53         为什么要减去len-1呢?原因是“0”这种情况,0在这里的表示是:000……00(len个0)。对于一个数
54         我们枚举每个位置作为平衡点,去判断是否存在一个位置使得这个数左右平衡。对于非0的数,如果
55         它是平衡数,那么它的平衡点只有一个,所以是+1,正好一一对应. 但对于000……00来说,它的每个
56         位置都可以作为平衡点( 因为左右两边都等于0),所以是+len,但是000……00却只是一个数,所以
57         需要减去重复计算的部分,即len-1.
58     */
59     return ret-(len-1);
60 }
61 
62 int main()
63 {
64     LL T, n, m;
65     scanf("%lld", &T);
66     memset(dp,-1,sizeof(dp));
67     while(T--)
68     {
69         scanf("%lld%lld",&m,&n);
70         LL ans = solve(n) - solve(m-1);
71         printf("%lld\n", ans);
72     }
73     return 0;
74 }
View Code

 

posted on 2017-11-28 18:35  h_z_cong  阅读(200)  评论(0编辑  收藏  举报

导航