HDU-4734 F(x) 数位dp+思维

本文介绍了一种利用记忆化搜索解决特定计数问题的方法,通过定义dp数组来避免重复计算,解决了多组样例数据下快速计算的问题。文章详细解释了如何设计dp状态转移方程,以及如何调整dp数组的大小和初始化策略以适应不同规模的输入数据,确保在严格的时间限制内得到正确的答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里是题目链接 F(x)               [Submit]

F(x)

Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11388    Accepted Submission(s): 4417

 

Problem Description

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

题意:定义f(x) = a(n)*2^(n-1)+a(n-1)*2(n-2)+…a(2)*2+a(1)*1,a(i)表示十进制数x中第i位的数字。 
题目给出a,b,求出0~b有多少个不大于f(a)的数。

这真是一道奇怪的题目,公式已经告诉你了,但是就是不好做,看着公式去枚举吗?你想啥呢?请看Time Limit  500ms!!!

看到这里我也是惊了,才500ms还要跑多组样例。。。

这就要谨慎的发挥我们记忆化搜索的神威了。而且还要是那种各个样例之间通用的记忆化dp数组

刚开始想着dp[pos][sum]不就完事了,sum记录从len到pos位上数字累计的权重
我们定义dp[pos][sum]的话,但是我们这T组数据……

每组数据只要B有变化,由于sum是从最高位往下累加权重,它跟B有密切关系(B的长度len即为最高位)
那么dp数组就要重新memset(dp,-1,sizeof(dp)),这样才不会出错;
但是你不断地memset的话500ms可定扛不住,那就只能换一种方法喽

那么如果sum代表从第1位到第pos位最多还能累加起多少权重,那么它就和B没什么关系
我们就不需要在输入每组数据后都重新将DP数组全部重置为-1

这就是这道题目最神奇的地方。。。

这种思维方式可以记下来,说不定什么时候就能用的着

还有就是dp数组的大小,开小了就会被TLE,难受

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
const int mm=20;
int dp[mm][5000];
int a[mm],b[mm];
int xx;

int dfs(int pos,int sum,int limit){
	if(pos<0)return 1;
	if(!limit&&~dp[pos][sum])
		return dp[pos][sum];
	int res=0;
	int up=limit?b[pos]:9;
	for(int i=0;i<=up;i++){
		if(sum<i*(1<<pos))break;
		int temp=sum-i*(1<<pos);
		res+=dfs(pos-1,temp,limit&&i==b[pos]);
	}
	if(!limit)dp[pos][sum]=res;
	return res;
}

int get(int x){
	int cnt=0;
	int ans=0;
	while(x){
		a[cnt++]=x%10;
		x/=10;
	}
	for(int i=0;i<cnt;i++){
		ans+=a[i]*(1<<i);
	}
	return ans;
}

int solve(int x){
	
	int cnt=0;
	while(x){
		b[cnt++]=x%10;
		x/=10;
	}
	return dfs(cnt-1,xx,1);
}

int main()
{
	int t;
	scanf("%d",&t);
	int x,y,test=1;
	mem(dp,-1);
	while(t--){
		scanf("%d%d",&x,&y);
		xx=get(x);
		int res=solve(y);
		printf("Case #%d: %d\n",test++,res);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值