zoukankan      html  css  js  c++  java
  • 数位DP

    1、CF 55D Beautiful numbers

      题意:如果一个数能被自己各个位的数字整除,那么它就叫 Beautiful numbers。求区间 [a,b] 中 Beautiful numbers 的个数。

      思路:一个数能被它的所有非零数位整除,则能被它们的最小公倍数整除,而1到9的最小公倍数为2520, 数位DP时我们只需保存前面那些位的最小公倍数就可进行状态转移,到边界时就把所有位的lcm求出了, 为了判断这个数能否被它的所有数位整除,我们还需要这个数的值,显然要记录值是不可能的,其实我们只 需记录它对2520的模即可,这样我们就可以设计出如下数位DP:dfs(pos,mod,lcm,f),pos为当前位,mod为前面那些位对2520的模,lcm为前面那些数位的最小公倍数,f标记前面那些位是否达到上限, 这样一来dp数组就要开到19*2520*2520,明显超内存了,考虑到最小公倍数是离散的,1-2520中可能 是最小公倍数的其实只有48个,经过离散化处理后,dp数组的最后一维可以降到48,这样就不会超了。

     

     1 #include <stdio.h>
     2 #include <iostream>
     3 #include <map>
     4 #include <set>
     5 #include <list>
     6 #include <stack>
     7 #include <vector>
     8 #include <math.h>
     9 #include <string.h>
    10 #include <queue>
    11 #include <string>
    12 #include <stdlib.h>
    13 #include <algorithm>
    14 #define LL long long
    15 #define eps 1e-12
    16 #define PI acos(-1.0)
    17 using namespace std;
    18 const int INF = 0x3f3f3f3f;
    19 const int maxn = 4010;
    20 const int max_lcm = 2520;
    21 
    22 LL gcd(LL a, LL b)
    23 {
    24     if (b == 0)
    25         return a;
    26     return gcd(b, a%b);
    27 }
    28 LL lcm(LL a, LL b)
    29 {
    30     return a / gcd(a, b)*b;
    31 }
    32 int dig[25];
    33 LL dp[25][50][2525];//经过分析后可以设出dp[20][2050][2050],dp[i][j][k]表示处理到i位,前面的数的最小公倍数为j,前面的数 % 2520为k。因为1~9组成的最小公倍数只有48个,可以离散化,这样数组就降到了dp[20][50][2520]。
    34 int Hash[2525];
    35 
    36 LL dfs(int len, int prelcm, int prenum, int up)
    37 {
    38     if (len == 0)
    39     {
    40         return prenum%prelcm == 0;
    41     }
    42     if (!up && dp[len][Hash[prelcm]][prenum] != -1)
    43         return dp[len][Hash[prelcm]][prenum];
    44     int end = up ? dig[len] : 9;
    45     LL res = 0;
    46     for (int i = 0; i <= end; i++)
    47     {
    48         int nownum = (prenum * 10 + i) % max_lcm;
    49         int nowlcm = prelcm;
    50         if (i)
    51             nowlcm = lcm(prelcm, i);
    52         res += dfs(len - 1, nowlcm, nownum, up&&i == end);//flag&&i==end,在最开始,取出的end是最高位,所以如果i比end小,那么i的下一位都可以到达9,而i==num了,最大能到达的就只有,dig[pos-1] 
    53     }
    54     if (!up)
    55         dp[len][Hash[prelcm]][prenum] = res;
    56     return res;
    57 }
    58 
    59 LL cal(LL num)
    60 {
    61     int len = 0;
    62     while (num)
    63     {
    64         dig[++len] = num % 10;
    65         num /= 10;
    66     }
    67     return dfs(len, 1, 0, 1);
    68 }
    69 
    70 int main()
    71 {
    72     int test;
    73     LL a, b;
    74     int cnt = 0;
    75     for (int i = 1; i <= 2520; i++) //离散化
    76     {
    77         if (max_lcm % i == 0)
    78             Hash[i] = ++cnt;
    79     }
    80 
    81     scanf("%d", &test);
    82     memset(dp, -1, sizeof(dp));
    83     for (int item = 1; item <= test; item++)
    84     {
    85         scanf("%I64d %I64d", &a, &b);
    86         printf("%I64d
    ", cal(b) - cal(a - 1));
    87     }
    88 
    89     return 0;
    90 }
    View Code

     

    2、HDU 4352 XHXJ’s LIS

      题意:假设把一个数字当成字符串,将它的最长单调递增子序列长度称为power值,求[L,R]区间内power值等于k(1<=K<=10)的值有多少个。

      思路:dp[i][j][k]:i为当前进行到的数位,j状态压缩,为10个数字出现过的,其中1的个数就是最长上升子序列,k要求的上升子序列的长度。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define LL __int64
     5 using namespace std;
     6 LL dp[40][1<<10][11];//dp[i][j][k]:i为当前进行到的数位,j状态压缩,为10个数字出现过的,其中1的个数就是最长上升子序列,k要求的上升子序列的长度
     7 int bit[40], pos;
     8 LL L, R; int K;
     9 int numone(int state)
    10 {//得到1的个数,即最长递增子序列的长度
    11     int ret = 0;
    12     while (state)
    13     {
    14         ret += state & 1; state >>= 1;
    15     }
    16     return ret;
    17 }
    18 int turn(int state, int x)
    19 {//找到第一个大于x的数并且替换他,nlogn求最长递增子序列的思想
    20 
    21     for(int i=x;i<=9;i++)
    22         if ((1<<i)&state)
    23         {
    24             return ((state^(1<<i)) | (1<<x));
    25         }
    26     return (state | (1<<x));
    27 }
    28 LL DP(int pp, int state, bool nozero, bool big)//has 是否有inc==K
    29 {//nozero为前面的是否为0标记,big上界标记
    30     if (pp == 0)return numone(state) == K;
    31     if (big&&dp[pp][state][K] != -1)return dp[pp][state][K];
    32     LL ret = 0;
    33     int kn = big ? 9 : bit[pp];
    34     for(int i=0;i<=kn;i++)
    35     {
    36         ret += DP(pp - 1, (nozero || i != 0) ? turn(state, i) : 0, nozero || i != 0, big || kn != i);
    37     }
    38     if (big)dp[pp][state][K] = ret;
    39     return ret;
    40 }
    41 LL get(LL x)
    42 {
    43     pos = 0;
    44     while (x)
    45     {
    46         bit[++pos] = x % 10;
    47         x /= 10;
    48     }
    49     return DP(pos, 0, 0, 0);
    50 }
    51 int main()
    52 {
    53     int t; 
    54     memset(dp, -1, sizeof(dp));
    55     scanf("%d", &t);
    56     int Case = 1;
    57     while (t--)
    58     {
    59             scanf("%I64d%I64d%d", &L, &R, &K);
    60             printf("Case #%d: %I64d
    ", Case++, get(R) - get(L - 1));
    61     }
    62     return 0;
    63 }
    View Code

     

  • 相关阅读:
    WinForm 无边框窗体 拖动工作区移动窗体
    [CSS]火狐和IE对css样式解释的差异
    md类型文件迁移至Notion(img资源也可以上传)
    google推出notebook软件
    刚收到几个google analytics 邀请,有真正需要的我可以送给他一个
    最近用drupal做了一个CMS网站
    google adsense 又增加了Picasa的推介,我已加上
    googlepages空间的一个bug
    近段时间比较郁闷
    开复与学生面对面
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7368185.html
Copyright © 2011-2022 走看看