HDU4734 F(x)

For a decimal number x with n digits (A  nn-1n-2 ... A  21), we define its weight as F(x) = A  n * 2  n-1 + A  n-1 * 2  n-2 + ... + A  2 * 2 + A  1 * 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 < 10  9)
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


人家说是入门级的,可我怎么想也没想出来,我知道优化的方向就是dp的初始化应该只有一次就好了,然而就是没想到转化一下思维。

一开始第二维表示的是当前数的价值的和,然后dp数组记忆化搜索的作用就显得不是那么大了。因为你存和跟你的A的价值的限制是有关系的,每次dp都初始化的话,作用就大打折扣了。

这时候就需要转化一下思维,第二维表示,价值不超过这个数的数的个数,这样,下一次相同的A出现的时候可以直接用。A变得时候不会影响到dp的状态的。

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
int a,b;
int dp[20][4600];
int digit[20];
int f(int x)
{
    int sum = 0;
    int p = 1;
    while(x)
    {
        sum += (x%10)*p;
        x /= 10;
        p <<= 1;
    }
    return sum;
}
int dfs(int len,int sum,bool up)
{
    if(sum < 0)return 0;
    if(len == -1)return sum >= 0;
    if(!up && dp[len][sum] != -1)return dp[len][sum];
    int res = 0;
    int n = up?digit[len] : 9;
    for(int i = 0 ; i <= n ; ++i)
    {
        res += dfs(len - 1,sum - i*(1<<len),up && i==n);
    }
    if(!up)dp[len][sum] = res;
    return res;
}
int cal(int x)
{
    int len = 0;
    while(x)
    {
        digit[len++] = x % 10;
        x /= 10;
    }
    return dfs(len - 1,f(a),1);
}
int main()
{
    int t;
    scanf("%d",&t);
    memset(dp,-1,sizeof dp);
    for(int tt = 1 ; tt <= t ; ++tt)
    {
        scanf("%d%d",&a,&b);
        printf("Case #%d: %d\n",tt,cal(b));
    }
    return 0;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值