zoukankan      html  css  js  c++  java
  • 【hdu4734】【F(x)】数位dp + 小小的总结一下

    (https://www.pixiv.net/member_illust.php?mode=medium&illust_id=65608478)

    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

     
    (终于找到时间写数位dp了。。)
     
    先总结一下数位dp的套路。
    通常情况下,数位dp用于统计个数,其实是暴力枚举的优化。
     
    想想面对一道数位dp的题,如果暴力做会怎么做?for每一个数,判断是否合法。但是我们发现:例如当枚举到 23456 和 33456 时,后面的部分“3456”是相同的,也就是说我们多枚举了很多次相同的情况,这时候就可以考虑用dp(记忆化)来优化。当需要用上一个状态很多次的时候,就可以考虑dp。
     
    所以数位dp的套路一般是:
    f[pos][...],dfs(pos,...,limit,[zero])
    表示:当前位,一些需要用到的状态,是否顶着上限,[是否有前导零(根据题目需要)]
    当没有限制的时候,就记忆化
     
    而这道题也符合这个套路,只不过状态稍微和平常的不一样。
    通常的状态是和前面的位置上的值有关,但是考虑这道题该如何储存状态才能方便转移呢?
    首先,这个二进制一定有鬼!(但是想偏了)发现数据最高位才9,29是一个很小的数。而这道题的比较对象是一个数值,所以转移的状态需要和数值有关。思考当我们已经枚举了前i位分别是那些数,相当于已经得到了前i位的fi值,如果要优化,就是直接调用“后面的数中的f值小于f(a)-fi的个数”。
     
    所以状态记录为:
    f[pos][sum]
    表示第pos位,后面的数的值小于等于sum的数的个数
     
    这样就很简单啦
    (但是思路还是很巧妙的,所以要总结一下)
     
     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 int f[11][10000],a,b,len,orz[11],sum,mi[11];
     7 
     8 int dfs(int pos,int pre,bool limit){
     9     if(pos==0){
    10         if(pre<=sum) return 1;
    11         return 0;
    12     }
    13     if((!limit)&&f[pos][sum-pre]!=-1) return f[pos][sum-pre];
    14     int st=limit?orz[pos]:9;
    15     int ans=0;
    16     for(int i=0;i<=st;i++)
    17         if(pre+i*mi[pos]<=sum) ans+=dfs(pos-1,pre+i*mi[pos],limit&&i==st);
    18     if(!limit) f[pos][sum-pre]=ans;
    19     return ans;
    20 }
    21 int main(){
    22     memset(f,-1,sizeof(f));
    23     mi[1]=1;
    24     for(int i=2;i<=10;i++) mi[i]=mi[i-1]*2;
    25     int t;
    26     scanf("%d",&t);
    27     for(int k=1;k<=t;k++){
    28         scanf("%d%d",&a,&b);
    29         sum=0;
    30         for(int i=a,j=1;i;i/=10,j++) sum+=mi[j]*(i%10);//printf("sum=%d
    ",sum);
    31         for(len=0,b;b;b/=10) orz[++len]=b%10;
    32         printf("Case #%d: %d
    ",k,dfs(len,0,1)); 
    33     }
    34     return 0;
    35 } 
    View Code
  • 相关阅读:
    javascript中的throttle和debounce
    移动端Click300毫秒点击延迟的来龙去脉(转)
    HTTP Keep-Alive详解[转]
    深入浅出requireJS-1
    判断网站是微信打开的
    2015腾讯和阿里前端实习生面试经
    解决ant design 中 select的option 随页面滚动条滚动的bug
    解决window.close()方法兼容各个浏览器(ie关闭会进行弹框提示是否关闭)
    多维数组遍历添加新属性,以及获取所有key
    Vue 路由解耦与快捷新增
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7780555.html
Copyright © 2011-2022 走看看