现在的位置: 首页 > 综合 > 正文

HDU 3709 Balanced Number

2013年12月23日 ⁄ 综合 ⁄ 共 3384字 ⁄ 字号 评论关闭
Balanced Number
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 162 Accepted Submission(s): 59

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int num[20], ln;
__int64 f[20][20][406];
__int64 g[20][91][406];
int maxs[20][20];

__int64 dfs(int i, int pt, int pres, int sum, bool flag){
	int high, p;
	__int64 ret;
	if (i <= 0) return sum == 0 && pt;
	if (sum < 0 || sum > 405) return 0;
	if (!pt && sum > maxs[i][0]) return 0;
	if (pt && sum > maxs[i][pt - i]) return 0;
	if (pt && !flag && ~f[i][pt][sum]) return f[i][pt][sum];
	if (!pt && !flag && ~g[i][pres][sum]) return g[i][pres][sum];
	
	high = flag ? num[i] : 9;
	ret = 0;
	for (p = 0; p <= high; p++){
		if (!pt){
			ret += dfs(i - 1, pt, pres + p, sum + pres + p, flag && (p == high));
			if (sum || p) ret += dfs(i - 1, i, 0, sum, flag && (p == high));
		}else{
			ret += dfs(i - 1, pt, 0, sum - p * (pt - i), flag && (p == high));
		}
	}
	if (!flag && pt) f[i][pt][sum] = ret;
	if (!flag && !pt) g[i][pres][sum] = ret;
	return ret;
}

__int64 calc(char *a){
	int i, j, la;
	la = strlen(a);
	for (i = 0; i < la && a[i] == '0'; i++);
	for (j = la - 1; j >= i; j--)
//		num[j - i + 1] = a[j] - '0';
		num[la - j] = a[j] - '0';
	ln = la - i;
	return dfs(ln, 0, 0, 0, true) + 1;
}

bool Dec(char *a){
	int t, la;
	la = strlen(a);
	t = la - 1;
	while(t >= 0 && a[t] == '0'){
		a[t] = '9';
		t--;
	}
	if (t >= 0) a[t]--;
	else return false;
	return true;
}

int main(){
	char a[20], b[20];
	int k, T;
	__int64 ans;
	memset(f, -1, sizeof(f));
	memset(g, -1, sizeof(g));
	memset(maxs, 0, sizeof(maxs));
	for (k = 1; k <= 20; k++){
		maxs[k][0] = maxs[k - 1][1];
		maxs[k][1] = maxs[k - 1][1] + 9 * k;
		for (T = 2; T <= 20 - k; T++){
			maxs[k][T] = maxs[k][T - 1] + 9 * k;
		}
	}
	scanf("%d", &T);
	for (k = 1; k <= T; k++){
		scanf("%s %s", a, b);
		if (!Dec(a)) ans = calc(b);
		else ans = calc(b) - calc(a);
		printf("%I64d\n", ans);
	}
	return 0;
}
/***************************
数位DP
经过上次经验,用记忆化搜索实现。
开始本能地会去想设俩个状态,前i位达到上限的,和后i位未达上限的,然后拼起来计算结果。
但是由于上次痛苦的教训,还是直接记忆化搜索,记录下搜索状态,来的简单,暴力,不容易错。。。
dfs(i, pt, pres, sum)
枚举第i位,此时轴是pt,
对于轴之前的i,pres = 前i位各位数和,sum = 各位数*距离的和
对于轴之后的i,pres 没用了直接设为0,sum 是还剩多少
也就说轴之前,sum是累加的,之后,sum就开始累减,能减到0,就是平衡的
注意,累加时,每向后移动一位,sum != sum * 2 + num[i], sum = sum + pres + num[i]

pt = 0, 表示轴还未确定,或者说轴在后i位中

本想直接记录f[i][pt][pres][sum], 结果果真MLE了...
记录f[i][pt][sum]呢? 会发现当pt = 0时,pres不同,会导致后面的结果不同,因为后的sum是依靠pres的
若判下pt!=0时,记录f[i][pt][sum],这样确实对,但丢了许多记录,最终TLE...
于是又想了各种剪枝,比如记录后i位能组成的最大和,若此时sum比它还大,显然不平衡之类的...
然而这只是杯水车薪,照样TLE...此后就在小心翼翼的MLE和TLE中交错度过...
最后,把pt = 0 和 pt != 0 的情况分开记录,因为pt = 0的时候需要记录pres,而pt != 0的不需要
这样,既没MLE,也没TLE,红红Accept从一堆绿中脱颖而出,有些刺眼...
想想也是,g记录轴之前的,f记录轴之后的,不记g的话,纯暴力枚举9位数甚至更多...

另外,把数字倒过来存,dfs时当flag=false时,值会与这数本身无关,
这样只要在一开始初始化f,g,往后就可以反复利用,不论输入怎么变

总之,跑31ms,比之前5000ms+,好的不是一星半点,还是挺满意的...

经杨大牛指点,在递归外枚举轴的位置,可以大大简化思维复杂度...
也不用考虑MLE什么的...

另一种判定思路,假设轴在最左边,计算力矩和sum,数字和pres,
考虑平衡度量bla = sum - 0, 注意到每向右移动一次轴,
相当于bla = (sum - x) - (0 + pres - x) = bla - pres
及,只要轴在最左边时,sum % pres == 0,即可判定数字balance 
****************************/


抱歉!评论已关闭.