【SPOJ-KOPC12H】K12-OE Numbers【数位DP】

题意:

定义OE数为:对于所有数位,使得偶数和大于奇数和的数。

比如1233,奇数和 = 1 + 3 + 3 = 7,偶数和 = 2,奇数和 > 偶数和,不是OE数。


设f[i][k]表示长度为i的数,偶数和减奇数和等于k的OE数的个数。

对于当前位j,

如果j为奇数,那么f[i][k] += f[i - 1][k + j]

如果j为偶数,那么f[i][k] += f[i - 1][k - j]


求个前缀和f[i][j] += f[i][j + 1],变为 f[i][k]表示长度为i的数,偶数和减奇数和大于等于k的OE数的个数。


计算的时候定义一个sum,表示已经计算过的位数的偶数和减奇数和。

统计答案时我们只需要让偶数和减奇数和大于0(大于等于1)就好,即对于当前位j,

如果j为奇数,ans += f[i - 1][-sum + j + 1]

如果j为偶数,ans += f[i - 1][-sum - j + 1]


注意边界情况,还有负下标的处理。


#include <cstdio>

const int maxn = 15;

int *f[maxn], A[maxn], tmp[maxn][200];

inline int get(int x, int y) {
	if(x) return f[x][y];
	return y <= 0;
}

inline int calc(int x) {
	if(x == 100000000) x--;
	if(x == 0) return 0;

	int cnt = 0;
	for(; x; x /= 10) A[++cnt] = x % 10;

	int ans = 0, sum = 0;
	for(int i = cnt; i >= 1; i--) {
		for(int j = 0; j < A[i]; j++)
			if(j & 1) ans += get(i - 1, -sum + j + 1);
			else ans += get(i - 1, -sum - j + 1);
		if(A[i] & 1) sum -= A[i];
		else sum += A[i];
	}

	if(sum > 0) ans++;
	return ans;
}

int main() {
	for(int i = 0; i < 10; i++) f[i] = &tmp[i][100]; f[0][0] = 1;

	for(int i = 1; i <= 8; i++) for(int j = 0; j < 10; j++) for(int k = -72; k <= 72; k++)
		if(j & 1) f[i][k] += f[i - 1][k + j];
		else f[i][k] += f[i - 1][k - j];

	for(int i = 0; i <= 8; i++) for(int j = 71; j >= -72; j--) f[i][j] += f[i][j + 1];

	int T; scanf("%d", &T);
	while(T--) {
		int a, b; scanf("%d%d", &a, &b);
		printf("%d\n", calc(b) - calc(a - 1));
	}
	return 0;
}


你可能感兴趣的:(数位dp)