hdu 4734 F(x) 数位DP裸

Description

For a decimal number x with n digits (A nA n-1A n-2 … A 2A 1), 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


这道题是说要求在0到B里有多少个数的F值小于等于F(A),然后我们可以发现这题可以数位DP做,F值的定义是一个数的这一位乘以这一位的2次方
举个例子
1234 = 4 * 1 + 3 * 2 + 2 * 4 + 1 * 8

借这道题写一下数位DP的模板的套路
dfs函数里面传参
dfs( int len, int ... ,int lead, int limit )

有些题会有其他参数,有些题需要传lead,表示是否有前导0,limit通常表示这一位有没有限制,如果有限制的话就应该是这一位的上限,如果没有限制的话就是0-9

在函数里面我们通常是先设计返回的情况,比如len为-1的时候返回这个点是否满足,满足为1,不满足为0,再比如我们记忆化一个dp[i][j],当这个点没有limit限制并且没有前导零限制等各种限制时return这个dp[i][j],否则我们就求一个这一位的up值,表示这一位最大可以取什么,然后for语句取for一些状态然后继续dfs下去,最后在没有限制的情况下把答案赋值给dp数组,也就是记忆化数组里面,最后再return即可,通常我们会在solve函数里面先把数位分解为我们想要的样子放到数组里面,十进制分解,二进制分解等等,放到数组里面,记录长度然后再传入dfs函数中,最后计算得出答案返回到主函数中


这道题其实就是一道裸题,按照上面说的思路来就可以了

#include <bits/stdc++.h>
using namespace std; 
int len, dp[11][ 50000 + 10 ], a[25];

int F( int n ) {
    int tmp = 0, BASE = 1;
    while( n ) {
        tmp += n % 10 * BASE;
        BASE *= 2;
        n /= 10;
    }
    return tmp;
}

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

int solve( int A, int B ) {
    len = 0;
    while(B) {
        a[len++] = B % 10;
        B /= 10;
    }
    return dfs( len - 1, F(A), 1 );
}

int main( ) { int T, A, B;
    scanf( "%d", &T );
    memset( dp, -1, sizeof(dp) );
    for( register int Emmmm = 1; Emmmm <= T; Emmmm++ ) {
        scanf( "%d%d", &A, &B );
        printf("Case #%d: %d\n", Emmmm, solve( A, B ) );
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值