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

    这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪。

    http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html

    1 int dfs(int i, int s, bool e) {
    2     if (i==-1) return s==target_s;
    3     if (!e && ~f[i][s]) return f[i][s];
    4     int res = 0;
    5     int u = e?num[i]:9;
    6     for (int d = first?1:0; d <= u; ++d)
    7         res += dfs(i-1, new_s(s, d), e&&d==u);
    8     return e?res:f[i][s]=res;
    9 }

    f为记忆化数组;

    i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);

    s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);

    e表示之前的数是否是上界的前缀(即后面的数能否任意填)。

    for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.

    于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。

    HDU2098 不要62

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 15
    11 int dp[maxn][2],num[maxn];
    12 int new_s(int s, int d)
    13 {
    14     if (d == 6) return 1;
    15     else
    16     {
    17         return 0;
    18     }
    19 }
    20 int dfs(int i, int s, bool e) {
    21     if (i == -1) return 1;
    22     if (!e && ~dp[i][s]) return dp[i][s];
    23     int res = 0;
    24     int u = e ? num[i] : 9;
    25     for (int d = 0; d <= u; ++d)
    26     {
    27         if (d==4) continue;
    28         if (s&&d==2) continue;
    29         res += dfs(i - 1, new_s(s, d), e && d == u);
    30     }
    31     return e ? res : dp[i][s] = res;
    32 }
    33 int cal(int n)
    34 {
    35     int cnt = 0;
    36     while (n)
    37     {
    38         num[cnt++] = n % 10;
    39         n /= 10;
    40     }
    41     return dfs(cnt - 1, 0,1);
    42 }
    43 int main()
    44 {
    45     int l, r;
    46     memset(dp, -1, sizeof(dp));
    47     while (scanf("%d%d", &l, &r) != EOF&&l+r)
    48     {
    49         printf("%d
    ", cal(r) - cal(l -1));
    50     }
    51     return 0;
    52 }
    View Code

    HDU3555 Bomb

    和上题差不多

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 30
    11 typedef long long LL;
    12 LL dp[maxn][3];
    13 int num[maxn];
    14 int new_s(int s, int d)
    15 {
    16     if (s == 2) return 2;
    17     if (s == 1 && d == 9) return 2;
    18     if (d == 4) return 1;
    19     return 0;
    20 
    21 }
    22 LL dfs(int i, int s, bool e)
    23 {
    24     if (i == -1) return s == 2;
    25     if (!e&&~dp[i][s]) return dp[i][s];
    26     LL ret = 0;
    27     int u = e ? num[i] : 9;
    28     for (int d = 0; d <= u; d++)
    29         ret += dfs(i - 1, new_s(s, d), e&&d == u);
    30     return e ? ret : dp[i][s] = ret;
    31 
    32 }
    33 LL cal(LL n)
    34 {
    35     int cnt = 0;
    36     while (n)
    37     {
    38         num[cnt++] = n % 10;
    39         n /= 10;
    40     }
    41     return dfs(cnt - 1, 0, 1);
    42 }
    43 int main()
    44 {
    45     int T;
    46     scanf("%d", &T);
    47     memset(dp, -1, sizeof(dp));
    48     while (T--)
    49     {
    50         LL n;
    51         scanf("%I64d", &n);
    52         printf("%I64d
    ", cal(n));
    53     }
    54     return 0;
    55 }
    View Code

    BZOJ1026 windy数

    注意前导0

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 30
    11 typedef long long LL;
    12 LL dp[maxn][10];
    13 int num[maxn];
    14 LL dfs(int i, int s, bool e,int pre)
    15 {
    16     if (i == -1) return s == 1;
    17     if (!e&&~dp[i][pre]&&s) return dp[i][pre];
    18     LL ret = 0;
    19     int u = e ? num[i] : 9;
    20     for (int d = 0; d <= u; d++)
    21     if(!s||abs(pre-d)>=2)
    22     {
    23         ret += dfs(i - 1, s||d>0, e&&d == u, d);
    24     }
    25     if (!e&&s)dp[i][pre] = ret;
    26     return ret;
    27 }
    28     
    29 LL cal(LL n)
    30 {
    31     int cnt = 0;
    32     while (n)
    33     {
    34         num[cnt++] = n % 10;
    35         n /= 10;
    36     }
    37     return dfs(cnt - 1, 0, 1,11);
    38 }
    39 int main()
    40 {
    41     LL x,y;
    42     memset(dp, -1, sizeof(dp));
    43     while (scanf("%lld%lld", &x,&y)!=EOF)
    44     {
    45         printf("%lld
    ", cal(y)-cal(x-1));
    46     }
    47     return 0;
    48 }
    View Code

    HDU3652 B-number

    加一维,记录余数

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 30
    11 typedef long long LL;
    12 LL dp[maxn][15][3];
    13 int num[maxn];
    14 int new_s(int s, int d)
    15 {
    16     if (s == 2) return 2;
    17     if (s == 1 && d == 3) return 2;
    18     if (d == 1) return 1;
    19     return 0;
    20 }
    21 LL dfs(int i, int s, bool e,int r)
    22 {
    23     if (i == -1)
    24     {
    25         if ((s== 2) && (r== 0)) return 1;
    26         else return 0;
    27     }
    28     if (!e&&~dp[i][r][s]) return dp[i][r][s];
    29     LL ret = 0;
    30     int u = e ? num[i] : 9;
    31     for (int d = 0; d <= u; d++)
    32     {
    33         ret += dfs(i - 1,new_s(s,d) , e&&d == u, (r*10+d)%13);
    34     }
    35     return e ? ret : dp[i][r][s] = ret;
    36 }
    37     
    38 LL cal(LL n)
    39 {
    40     int cnt = 0;
    41     while (n)
    42     {
    43         num[cnt++] = n % 10;
    44         n /= 10;
    45     }
    46     return dfs(cnt - 1, 0, 1,0);
    47 }
    48 int main()
    49 {
    50     LL n;
    51     memset(dp, -1, sizeof(dp));
    52     while (scanf("%I64d",&n)!=EOF)
    53     {
    54         printf("%I64d
    ", cal(n));
    55     }
    56     return 0;
    57 }
    View Code

    HDU3943 K-th Nya Number

    需要二分答案,还有就是注意区间范围是[P+1,Q],被坑了好多发,还有我很不解的就是,memset放在最外面就会WA,每输入一组案例清空一次就AC了。。坑爹。。

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 25
    11 typedef long long LL;
    12 LL dp[maxn][maxn][maxn];
    13 LL P, Q, x, y;
    14 int num[maxn];
    15 LL dfs(int i, int sx, int sy, bool e)
    16 {
    17     if (i == -1)
    18     {
    19         if (sx == x&&sy == y) return 1; 
    20         else return 0;
    21     }
    22     if (!e&&~dp[i][sx][sy]) return dp[i][sx][sy];
    23     LL ret = 0;
    24     int u = e ? num[i] : 9;
    25     for (int d = 0; d <= u; d++)
    26     {
    27         if (sx == x&&d == 4) continue;
    28         if (sy == y&&d == 7) continue;
    29         int a, b;
    30         a = sx; b = sy;
    31         if (d == 4) a++;
    32         if (d == 7) b++;
    33         ret += dfs(i - 1, a, b, e&&d == u);
    34     }
    35     return e ? ret : dp[i][sx][sy] = ret;
    36 }
    37 LL cal(LL n)
    38 {
    39     int cnt = 0;
    40     while (n)
    41     {
    42         num[cnt++] = n % 10;
    43         n /= 10;
    44     }
    45     return dfs(cnt - 1, 0, 0, 1);
    46 }
    47 LL Bin(LL k)
    48 {
    49     LL l, r, mid, ans,ret;
    50     ans = 0;
    51     ret = cal(P);
    52     l = P+1; r = Q;
    53     while (l <= r)
    54     {
    55         mid = (l + r) >> 1;
    56         if (cal(mid) - ret>= k)
    57         {
    58             ans = mid;
    59             r = mid - 1;
    60         }
    61         else l = mid + 1;
    62     }
    63     return ans;
    64 }
    65 int main()
    66 {
    67     int T,kase=0;
    68     scanf("%d", &T);
    69     
    70     while (T--)
    71     {
    72         int n;
    73         memset(dp, -1, sizeof(dp));
    74         scanf("%I64d%I64d%I64d%I64d", &P, &Q, &x, &y);
    75         scanf("%d", &n);
    76         printf("Case #%d:
    ", ++kase);
    77         while (n--)
    78         {
    79             LL k;
    80             scanf("%I64d",&k);
    81             LL ans = Bin(k);
    82             if (ans)
    83             printf("%I64d
    ", ans);
    84             else printf("Nya!
    ");
    85         }
    86     }
    87     return 0;
    88 }
    View Code

    POJ3208 Apocalypse Someday

    做法和上题一样,需要注意的是它是必须有连续的三个6,还有就是二分的上界尽量大。。

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 30
    11 typedef long long LL;
    12 LL dp[maxn][4];
    13 int num[maxn];
    14 int new_d(int s, int d)
    15 {
    16     if (s == 3) return 3;
    17     int st = s;
    18     if (d == 6) s++;
    19     return st==s?0:s;
    20 }
    21 LL dfs(int i, int s,bool e)
    22 {
    23     if (i == -1) return s == 3;
    24     if (!e&&~dp[i][s]) return dp[i][s];
    25     LL ret = 0;
    26     int u = e ? num[i] : 9;
    27     for (int d = 0; d <= u; d++)
    28     {
    29         ret += dfs(i - 1, new_d(s,d),e&&d == u);
    30     }
    31     return e ? ret : dp[i][s] = ret;
    32 }
    33 LL cal(LL n)
    34 {
    35     int cnt = 0;
    36     while (n)
    37     {
    38         num[cnt++] = n % 10;
    39         n /= 10;
    40     }
    41     return dfs(cnt - 1, 0, 1);
    42 }
    43 LL Bin(LL k)
    44 {
    45     LL l, r, mid, ans,ret;
    46     ans = 0;
    47     l = 666, r = 100000000000LL;
    48     while (l <= r)
    49     {
    50         mid = (l + r) >> 1;
    51         if (cal(mid)>= k)
    52         {
    53             ans = mid;
    54             r = mid - 1;
    55         }
    56         else l = mid + 1;
    57     }
    58     return ans;
    59 }
    60 int main()
    61 {
    62     int T;
    63     scanf("%d", &T);
    64     while (T--)
    65     {
    66         LL k;
    67         memset(dp, -1, sizeof(dp));
    68         scanf("%I64d", &k);
    69         printf("%I64d
    ", Bin(k));
    70     }
    71     return 0;
    72 }
    View Code

    SPOJ BALNUM Balanced Numbers

    刚开始一直不知道该怎么记录前面的状态,搜了下解题报告,用的是三进制来表示前面的状态(此题的精华就是这里吧。。),因为状态总数为3^10,因此也不大。

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 66666
    11 typedef long long LL;
    12 LL dp[25][maxn];
    13 int num[25];
    14 int cnt[10];
    15 void go(int s)
    16 {
    17     for (int i = 0; i < 10; i++)
    18     {
    19         cnt[i] = s % 3;
    20         s /= 3;
    21     }
    22 }
    23 int new_s(int s, int d)
    24 {
    25     go(s);
    26     if (cnt[d] == 0) cnt[d] = 1;
    27     else
    28         cnt[d] = 3 - cnt[d];
    29     int base = 1;
    30     s = 0;
    31     for (int i = 0; i < 10; i++)
    32     {
    33         s += base * cnt[i];
    34         base *= 3;
    35     }
    36     return s;
    37 }
    38 int check(int s)
    39 {
    40     go(s);
    41     for (int i = 0; i < 10; i++)
    42     {
    43         if ((i & 1) && (cnt[i] == 1)) return 0;
    44         if (!(i & 1) && (cnt[i] == 2))return 0;
    45     }
    46     return 1;
    47 }
    48 LL dfs(int i, int s, bool e,int zero)
    49 {
    50     if (i == -1) return check(s);
    51     if (!e&&~dp[i][s]) return dp[i][s];
    52     LL ret = 0;
    53     int u = e ? num[i] : 9;
    54     for (int d = 0; d <= u; d++)
    55     {
    56         ret += dfs(i - 1, zero&&d==0?0:new_s(s, d), e&&d == u,zero&&d==0);
    57     }
    58     return e ? ret : dp[i][s] = ret;
    59 }
    60 LL cal(LL n)
    61 {
    62     int cnt = 0;
    63     while (n)
    64     {
    65         num[cnt++] = n % 10;
    66         n /= 10;
    67     }
    68     return dfs(cnt - 1, 0, 1,1);
    69 }
    70 int main()
    71 {
    72     int T;
    73     scanf("%d", &T);
    74     memset(dp, -1, sizeof(dp));
    75     while (T--)
    76     {
    77         LL x, y;
    78         scanf("%lld%lld", &x, &y);
    79         printf("%lld
    ", cal(y) - cal(x - 1));
    80     }
    81     return 0;
    82 }
    View Code

    SPOJ MYQ10 Mirror Number

    每个数字只可能是0,1,8,区间比较大0 <= a<=b <= 10^44,所以输入要用字符串,一般我们求答案都是:cal(b)-cal(a-1),但此题是字符串,因此需要特殊下a是不是Mirror Number,还被坑了好久的就是,由于是字符串输入,最高位的下标是0。。然后我没有反转过来。。找了好久才发现。。用一个数组记录前面选的数(之前的状态),只要第一个非0位确定就可以知道回文串的长度,也就知道回文串中心的位置,然后从中心更低的位置开始判断是不是回文。前导0也需要注意

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <utility>
     6 #include <vector>
     7 #include <queue>
     8 using namespace std;
     9 #define INF 0x3f3f3f3f
    10 #define maxn 50
    11 typedef long long LL;
    12 LL dp[maxn][maxn];
    13 int num[maxn],tmp[maxn];
    14 LL dfs(int i, int len, bool e,int zero)
    15 {
    16     if (i == -1) return 1;
    17     if (!e&&~dp[i][len]) return dp[i][len];
    18     LL ret = 0;
    19     int u = e ? num[i] : 9;
    20     for (int d = 0; d <= u; d++)
    21     {
    22         if (d!=0&&d!=1&&d!=8) continue;
    23         if (zero)
    24         {
    25             tmp[i] = d;
    26             ret += dfs(i - 1, len - !d, e&&d == u, zero&& d == 0);
    27         }
    28         else
    29         {
    30             int mid = len / 2;
    31             int fg = i < mid ? 1 : 0;
    32             if (fg)
    33             {
    34                 if (tmp[len - i - 1] == d)
    35                     ret += dfs(i - 1, len, e&&d == u, zero);
    36             }
    37             else
    38             {
    39                 tmp[i] = d;
    40                 ret += dfs(i - 1, len, e&&d == u, zero);
    41             }
    42         }
    43     }
    44     return e ? ret : dp[i][len] = ret;
    45 }
    46 LL cal(char *s)
    47 {
    48     int cnt = strlen(s);
    49     for (int i = 0; i < cnt; i++) num[cnt-i-1] = s[i] - '0';
    50     return dfs(cnt - 1, cnt, 1,1);
    51 }
    52 int ok(char *s)
    53 {
    54     int len = strlen(s);
    55     for (int i = 0; i < len; i++)
    56         if (s[i] != '0'&& s[i] != '8'&&s[i] != '1') return 0;
    57     for (int i = 0; i < len / 2; i++)
    58         if (s[i] != s[len - i - 1]) return 0;
    59     return 1;
    60 }
    61 int main()
    62 {
    63     int T;
    64     scanf("%d", &T);
    65     memset(dp, -1, sizeof(dp));
    66     while (T--)
    67     {
    68         char x[maxn], y[maxn];
    69         scanf("%s%s", x, y);
    70         printf("%lld
    ", cal(y) - cal(x)+ok(x));
    71     }
    72     
    73     return 0;
    74 }
    View Code
  • 相关阅读:
    523 vue组件化高级 插槽slot:slot基本使用,具名插槽,编译作用域,作用域插槽
    522 vue父子组件的访问方式: $children、$refs、$parent
    521 vue子组件向父组件传递数据
    520 vue父组件向子组件传递:props基本用法,props数据验证,props中的驼峰标识
    在 React 组件中使用 Refs 指南
    Flutter 代码开发规范
    Angular ZoneJS 原理
    vue中$refs, $emit, $on, $once, $off的使用
    详解Js加法运算符
    JavaScript 异常
  • 原文地址:https://www.cnblogs.com/zjbztianya/p/3891196.html
Copyright © 2011-2022 走看看