hdu 4734 F(x)(数位dp)

一个十进制数字x,有n位数字,表示为AnAn-1….A1,定义 F(x)=An2n1+An12n2+...+A22+A11
现在给你A和B,计算在[0,B]之间有多少数字的F(num) <= F(A)
参考:http://blog.csdn.net/wust_zzwh/article/details/52100392
建议看看这个链接,里面讲的挺详细
先分析状态,F(x)就是计算每位的权值,dp[pos][sum]就是表示i位(包含前导0,即000023)的数字,这些数字的和<=sum的数字一共多少。由于A是题目给出的,所以每次询问的F(A)就是变化的,F(A)最大在4600左右,要想使得记忆化数组在每次询问的时候都能使用,则需要添加一个状态来标记不同的F(A),则记忆化数组为dp[10][4600][4600],数组太大,开不下,而且状态这么多,肯定超时。这时候就要换个思路了,dp[pos][sum]中的sum不再表示当前枚举过的数字的权值和,而是表示枚举到pos位置后,还需要sum的权值才能到达F(A),这样sum这个状态就和F(A)没关系了。

#include <stdio.h>
#include <string.h>

const int MAXN = 5e4+10;
int dp[12][MAXN];
int digit[12];
int len,all;

int f(int num)
{
    int ret = 0,cnt = 0;
    while(num)
    {
        ret += (num%10)*(1<<cnt);
        num /= 10;
        ++cnt;
    }
    return ret;
}

void calc(int num)
{
    len = 0;
    while(num)
    {
        digit[len++] = num%10;
        num /= 10;
    }
}

int dfs(int i, int s, bool e)
{
    if(i == -1) return s <= all;
    if(s > all) return 0;
    if(!e && ~dp[i][all-s]) return dp[i][all-s];
    int res = 0;
    int u = e?digit[i]:9;
    for(int d = 0; d <= u; ++d)
        res += dfs(i-1,s+d*(1<<i),e&&d==u);
    return e?res:dp[i][all-s]=res;
}

int solve(int num)
{
    calc(num);
    return dfs(len-1, 0, true);
}

int main()
{
    int T,a,b,time = 1;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&a,&b);
        all = f(a);
        printf("Case #%d: %d\n",time++,solve(b));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值