zoukankan      html  css  js  c++  java
  • 数位dp浅谈(hdu3555)

    数位dp简介:

    数位dp常用于求区间内某些特殊(常关于数字各个数位上的值)数字(比如要求数字含62,49);

    常用解法:

    数位dp常用记忆化搜索或递推来实现;

    由于记忆化搜索比较好写再加上博主比较蒟,所以本文基本只介绍用记忆化搜索实现的数位dp;

    记搜写法:

    一般记搜写法会暴力搜索每个数的每一位,如果满足特征就加入答案;

    而搜索中或搜完后用一个dp数组来存某一区间的特殊数的数量,防止多次重复搜索TLE;

    空口说比较苍白无力,举个例子:比如要在1到r中找含49(4和9要连在一起)的特殊数的数量;

    搜索时,传递当前要填的数字在数中的位置(pos),上一个填的数值(pre),之前有没有出现49(have),以及填的数有没有限制(limit);

    pos用来观察这个数有几位,有没有达到范围;

    pre用来判断之前的数是不是4,从而来判断如果当前位填9,是否能出现49

    have用记录是否出现 49,在搜的过程中就可以记录特殊数的数量;

    limit的作用是防止数过大超过范围(具体操作见下文例题);

    dp[pos][pre]存当当前要填的数的位置为pos,上一个数为pre时特殊值的数量;

    例题(hdu3555):

    题目大意是给出n,给出n个范围1—r,输出每个范围中含49的数的数量

    思路就是上面的例子,这里仔细介绍一下limit的用法;

    比如r=1234,当pos=3(pos=1时是个位,从第一位开始搜),如果pre=1,那么这一位就只能填0—2了,limit就是记录之前填的数和上界是否相同,而传递也很简单,如果limit=true而且当前要填的数等于给定范围的pos位上的数时,limit仍然是true,否则就是false;

    注意!

    1、数位dp基本上的题都要开long long,不然暴力就能过了;

    2、具体题目时要注意dp的含义防止重复加;

    下面附上丑陋的代码:

     1 #include<cstdio>
     2 using namespace std;
     3 #define int long long 
     4 const int MAXN=20;
     5 int n,r,t,digit[MAXN],dp[MAXN][MAXN];
     6 //digit是上界各个位置的数
     7 //dp记录搜过的值 
     8 int dfs(int pos,int pre,bool limit)
     9 //我这里的记搜和上面讲的略有不同,求的是不满足条件的数,如果出现49了就不继续做 
    10 //最后答案就是上界减去搜出来的数值 
    11 //这样可以在记搜时去除一维,加快一点速度 
    12 {
    13     if(pos==0) return 1; 
    14     if(!limit&&dp[pos][pre]!=0)
    15     //这里的!limit是因为如果当前填的数是有范围的(不能大于上界),就不满足一般的规律 
    16     {
    17         return dp[pos][pre];
    18     }
    19     int up=9;
    20     if(limit) up=degit[pos];
    21     //如果有限制就把上界设为范围的值 
    22     int ans=0;
    23     for(int i=0;i<=up;++i)
    24     if(pre==4&&i==9)
    25         continue;
    26     //满足条件就跳出 
    27     else
    28     {
    29         ans+=dfs(pos-1,i,limit&&(i==digit[pos]));
    30     }
    31     if(!limit)
    32     //和上面的!limit同一个道理 
    33     {
    34         dp[pos][pre]=ans;
    35     }
    36     return ans;
    37 }
    38 void solve(int x)
    39 {
    40     t=0;
    41     int xx=x;
    42     while(x>0)
    43     {
    44         ++t;
    45         digit[t]=x%10;
    46         x=x/10;
    47     }
    48     printf("%lld
    ",xx-dfs(t,0,1)+1);
    49 }
    50 main()
    51 {
    52     scanf("%lld",&n);
    53     for(int i=1;i<=n;++i)
    54     {
    55         scanf("%lld",&r);
    56         solve(r);
    57     }
    58 }
  • 相关阅读:
    C++中两种获取UUID的方法(编程)
    在python中发送自定义消息
    lib,dll的位置
    GetWindowText
    SuspendThread and ResumeThread
    创建线程检查按钮的状态
    C++检测句柄的权限
    POJ2186 强联通
    POJ2186 强联通
    POJ 1201 差分约束(集合最小元素个数)
  • 原文地址:https://www.cnblogs.com/JinLeiBo/p/9726732.html
Copyright © 2011-2022 走看看