[好题][数位dp]F(x) HDU4734

For a decimal number x with n digits (AnAn-1An-2 ... A2A1), we define its weight as F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A).

Input

The first line has a number T (T <= 10000) , indicating the number of test cases.
For each test case, there are two numbers A and B (0 <= A,B < 109)

Output

For every case,you should output "Case #t: " at first, without quotes. The t is the case number starting from 1. Then output the answer.

Sample Input

3
0 100
1 10
5 100

Sample Output

Case #1: 1
Case #2: 2
Case #3: 13

题意: 每组数据给出a,b,求[0,b]区间内小于等于f(a)的数字个数。f(x) = An * 2^n-1 + An-1 * 2^n-2 + ... + A2 * 2 + A1 * 1,Ax为十进制下各位数字。

分析: 显然是道数位dp题,可以定义dp数组为dp[15][10000],dp[i][j]表示枚举到第i位,f(a)减第i位之前的权值加和为j时符合条件的数字个数,这样即使不同数据下f(a)改变了也仍然可以用到之前记录的dp值,dp数组只需要一开始初始化一次即可。而定义dp第二维含义为第i位之前的权值加和为j就是错误的,这样要么每组数据前清空dp数组,要么dp数组开第三维记录当前的f(a),显然分别会导致TLE和MLE。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int c[15], dp[15][10000], a, b;//dp[i][j]表示fa减前i位权值和为j时符合条件的数字个数 

int dfs(int pos, int sum, int flag)
{
	if(pos == 0)
		return 1;
	if(flag && dp[pos][sum] != -1)
		return dp[pos][sum];
	int ans = 0, up = flag ? 9 : c[pos];
	for(int i = 0; i <= up; i++)
	{
		int t = i<<(pos-1);
		if(sum - t >= 0)
			ans += dfs(pos-1, sum-t, flag||i<up);
	}
	if(flag)
		dp[pos][sum] = ans;
	return ans;
}

int solve(int x)
{
	int pos = 0;
	do
	{
		c[++pos] = x%10;
		x /= 10;
	}while(x);
	int fa = 0, cnt = 0;
	do
	{
		fa += (a%10)<<cnt;
		a /= 10;
		cnt++;
	}while(a);
	return dfs(pos, fa, 0);
} 

signed main()
{
	int T;
	cin >> T;
	memset(dp, -1, sizeof dp);
	for(int i = 1; i <= T; i++)
	{
		scanf("%d%d", &a, &b);
		printf("Case #%d: %d\n", i, solve(b));
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值