zoukankan      html  css  js  c++  java
  • [转]数位dp小记

    转载自:http://blog.csdn.net/guognib/article/details/25472879

    参考:

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

    kuangbin :http://www.cnblogs.com/kuangbin/category/476047.html

    http://blog.csdn.net/cmonkey_cfj/article/details/7798809

    http://blog.csdn.net/liuqiyao_01/article/details/9109419

    数位dp有递推和记忆化搜索的方法,比较来说记忆化搜索方法更好。通过博客一的一个好的模板,数位dp就几乎变成一个线性dp问题了。

    下为博客一内容:

    。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

    偷看了下7k+大牛的数位统计dp写法,通常的数位dp可以写成如下形式:

    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.

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

    注意:

    不满足区间减法性质的话(如hdu 4376),不能用solve(r)-solve(l-1),状态设计会更加诡异。

    。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

    正如上面说的要注意:

    前导零是否有影响。

    是否满足区间减的性质。(如hdu4376,还没做,先记上)

    几乎就是模板题:

    模板:

    UESTC 1307相邻的数差大于等于2

    (不允许有前导0,前导0对计算有影响,注意前导0的处理)

     1 int dp[15][10];
     2 int bit[15];
     3 int dfs(int pos, int s, bool limit, bool fzero)
     4 {
     5      if (pos == -1) return 1;///前导0的影响!!!
     6     if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!!
     7     int end = limit ? bit[pos] : 9;
     8 
     9     int ans = 0;
    10     for (int i = 0; i <= end; i++)
    11     {
    12         if (!fzero && abs(i - s) < 2) continue;///前导0的影响!!!
    13         int nows = i;
    14         ans += dfs(pos - 1, nows, limit && (i == end), fzero && !i);
    15     }
    16 
    17     return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!
    18 }
    19 int calc(int n)
    20 {
    21     if (n == 0) return 1;
    22     int len = 0;
    23     while (n)
    24     {
    25         bit[len++] = n % 10;
    26         n /= 10;
    27     }
    28     return dfs(len - 1, 0, 1, 1);
    29 }
    30 int main ()
    31 {
    32     memset(dp, -1, sizeof(dp));
    33     int l, r;
    34     while (cin >> l >> r)
    35     {
    36         cout << calc(r) - calc(l - 1) << endl;
    37     }
    38     return 0;
    39 }
    View Code

    其它一些题目:

    Hdu3555不能出现连续的49

     1 int bit[25];
     2 LL dp[25][3];
     3 /**
     4 0
     5 1
     6 2
     7 */
     8 
     9 /**
    10 pos为当前考虑的位置
    11 s为在pos之前已到达的状态
    12 limit当前考虑的数字是否有限制,即之前已确定的数是否为n的前缀
    13 */
    14 LL dfs(int pos, int s, bool limit)
    15 {
    16     if (pos == -1) return s == 2;
    17     if (!limit && ~dp[pos][s]) return dp[pos][s];
    18     int end = limit ? bit[pos] : 9;///limit选择
    19     LL ans = 0;
    20 
    21     for (int i = 0; i <= end; i++)
    22     {
    23         int nows;
    24         if (s == 0)
    25         {
    26             if (i == 4) nows = 1;
    27             else nows = 0;
    28         }
    29         else if (s == 1)
    30         {
    31             if (i == 9) nows = 2;
    32             else if (i == 4) nows = 1;
    33             else nows = 0;
    34         }
    35         else if (s == 2) nows = 2;
    36 
    37         ans += dfs(pos - 1, nows, limit && i == end);
    38     }
    39 
    40     return limit ? ans : dp[pos][s] = ans;///limit选择
    41 }
    42 
    43 LL calc(LL n)
    44 {
    45     ///
    46     int len = 0;
    47     while (n)
    48     {
    49         bit[len++] = n % 10;
    50         n /= 10;
    51     }
    52     ///
    53     return dfs(len - 1, 0, 1);
    54 }
    55 int main ()
    56 {
    57     memset(dp, -1, sizeof(dp));
    58     int T;
    59     RI(T);
    60     LL n;
    61     while (T--)
    62     {
    63         cin >> n;
    64         cout << calc(n) << endl;
    65     }
    66     return 0;
    67 }
    View Code

    hdu2089 不要62 

     1 int dp[10][3];
     2 int bit[10];
     3 int dfs(int pos, int s, int limit, bool first)
     4 {
     5     if (pos == -1) return s == 2;
     6     if (!limit && ~dp[pos][s]) return dp[pos][s];
     7     int end = limit ? bit[pos] : 9;
     8 //    int be = first ? 1 : 0;
     9 
    10 
    11     int ans = 0;
    12     for (int i = 0; i <= end; i++)
    13     {
    14         int nows = s;
    15         if (s == 0)
    16         {
    17             if (i == 6) nows = 1;
    18             else if (i == 4) nows = 2;
    19         }
    20         if (s == 1)
    21         {
    22             if (i == 2 || i == 4) nows = 2;
    23             else if (i == 6) nows = 1;
    24             else nows = 0;
    25         }
    26         if (i == 4) nows = 2;
    27 
    28         ans += dfs(pos - 1, nows, limit && (i == end), first && !i);
    29     }
    30 
    31     return limit ? ans : dp[pos][s] = ans;
    32 }
    33 int calc(int n)
    34 {
    35     int tmp = n;
    36     if (n == 0) return 0;
    37     int len = 0;
    38     while (n)
    39     {
    40         bit[len++] = n % 10;
    41         n /= 10;
    42     }
    43     return tmp - dfs(len - 1, 0, 1, 1);
    44 }
    45 int main ()
    46 {
    47     memset(dp, -1, sizeof(dp));
    48     int l, r;
    49 //    for (int i = 1; i <= 100; i++)
    50 //        cout << i << ' ' << calc(i) << endl;
    51     while (cin >> l >> r)
    52     {
    53         if (l + r == 0) break;
    54 //        cout << calc(r) << ' ' << calc(l - 1) << endl;
    55         cout << calc(r) - calc(l - 1) << endl;
    56     }
    57 
    58     return 0;
    59 }
    View Code

    UESTC 1307相邻的数差大于等于2

    (注意前导0的处理)

     1 int dp[15][10];
     2 int bit[15];
     3 int dfs(int pos, int s, bool limit, bool fzero)
     4 {
     5      if (pos == -1) return 1;///前导0的影响!!!
     6     if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!!
     7     int end = limit ? bit[pos] : 9;
     8 
     9     int ans = 0;
    10     for (int i = 0; i <= end; i++)
    11     {
    12         if (!fzero && abs(i - s) < 2) continue;///前导0的影响!!!
    13         int nows = i;
    14         ans += dfs(pos - 1, nows, limit && (i == end), fzero && !i);
    15     }
    16 
    17     return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!
    18 }
    19 int calc(int n)
    20 {
    21     if (n == 0) return 1;
    22     int len = 0;
    23     while (n)
    24     {
    25         bit[len++] = n % 10;
    26         n /= 10;
    27     }
    28     return dfs(len - 1, 0, 1, 1);
    29 }
    30 int main ()
    31 {
    32     memset(dp, -1, sizeof(dp));
    33     int l, r;
    34     while (cin >> l >> r)
    35     {
    36         cout << calc(r) - calc(l - 1) << endl;
    37     }
    38     return 0;
    39 }
    View Code

    POJ 3252  Round Number (组合数)!!!

    拆成2进制,在记录0和1的个数

    求区间[l,r]中,满足传化成2进制后,0的个数>=1的个数的,数字的个数

     1 int dp[40][40][40];
     2 int bit[40];
     3 int dfs(int pos, int num0, int num1, bool limit, bool fzero)
     4 {
     5      if (pos == -1) return num0 >= num1;///前导0的影响!!!
     6     if (!limit && !fzero && ~dp[pos][num0][num1]) return dp[pos][num0][num1];///条件判断!!!
     7     int end = limit ? bit[pos] : 1;
     8 
     9     int ans = 0;
    10     for (int i = 0; i <= end; i++)
    11     {
    12         int new0, new1;
    13         if (fzero)
    14         {
    15             new0 = 0, new1 = 0;
    16             if (i) new1 = 1;
    17         }
    18         else
    19         {
    20             new0 = num0, new1 = num1;
    21             if (i) new1++;
    22             else new0++;
    23         }
    24         ans +=  dfs(pos - 1, new0, new1, limit && (i == end), fzero && !i);
    25     }
    26     return limit || fzero ? ans : dp[pos][num0][num1] = ans;///条件判断!!!
    27 }
    28 int calc(int n)
    29 {
    30     if (n == 0) return 1;
    31     int len = 0;
    32     while (n)
    33     {
    34         bit[len++] = n % 2;
    35         n /= 2;
    36     }
    37     return dfs(len - 1, 0, 0, 1, 1);
    38 }
    39 int main ()
    40 {
    41     memset(dp, -1, sizeof(dp));
    42     int l, r;
    43     while (cin >> l >> r)
    44     {
    45         cout << calc(r) - calc(l - 1) << endl;
    46     }
    47     return 0;
    48 }
    View Code

    hdu3886求满足符号串的数字个数!!!

    统计满足和指定 升降字符串 匹配的个数

     1 int dp[105][105][10];
     2 int bit[105];
     3 char s[105];
     4 char a[105], b[105];
     5 int ns;
     6 
     7 bool ok(int r, int a, int b)
     8 {
     9     if (s[r] == '/') return a < b;
    10     else if (s[r] == '-') return a == b;
    11     else if (s[r] == '\') return a > b;
    12 }
    13 int dfs(int pos, int r, int pre, bool limit, bool prezero)
    14 {
    15     if (pos == -1) return (r == ns);
    16     if (!limit && !prezero && ~dp[pos][r][pre]) return dp[pos][r][pre];
    17     int end = limit ? bit[pos] : 9;
    18     int ans = 0;
    19 
    20     for (int i = 0; i <= end; i++)
    21     {
    22         if (prezero)
    23         {
    24             ans += dfs(pos - 1, r, i, limit && (i == end), prezero && (!i));
    25         }
    26         else
    27         {
    28             if (r < ns && ok(r, pre, i))///优先考虑向后拓展
    29                 ans += dfs(pos - 1, r + 1, i, limit && (i == end), 0);
    30             else if (r > 0 && ok(r - 1, pre, i))
    31                 ans += dfs(pos - 1, r, i, limit && (i == end), 0);
    32         }
    33         ans %= MOD;
    34     }
    35     if (!limit && !prezero) dp[pos][r][pre] = ans;
    36     return ans;
    37 }
    38 int calc(char a[], bool dec)
    39 {
    40 
    41     int n = strlen(a);
    42     int len = 0, tmp = 0;
    43     while (a[tmp] == '0') tmp++;
    44     for (int i = n - 1; i >= tmp; i--)
    45     {
    46         bit[len++] = a[i] - '0';
    47     }
    48     if (dec && len > 0)
    49     {
    50         for (int i = 0; i < len; i++)
    51         {
    52             if (bit[i])
    53             {
    54                 bit[i]--;
    55                 break;
    56             }
    57             else bit[i] = 9;
    58         }
    59     }
    60     return dfs(len - 1, 0, 0, 1, 1);
    61 }
    62 
    63 int main ()
    64 {
    65     while (scanf("%s", s) != EOF)
    66     {
    67         memset(dp, -1, sizeof(dp));
    68         ns = strlen(s);
    69         scanf("%s%s", a, b);
    70         printf("%08d
    ", (calc(b, 0) - calc(a, 1) + MOD) % MOD);
    71     }
    72     return 0;
    73 }
    View Code

    HDU 3709 平衡数

     1 LL dp[20][20][2000];
     2 ///力矩最大为不超过10*20*10;
     3 int bit[20];
     4 
     5 LL dfs(int pos, int r, int sum, int e)
     6 {
     7     if (pos == -1) return sum == 0;
     8     if (sum < 0) return 0;
     9     if (!e && ~dp[pos][r][sum]) return dp[pos][r][sum];
    10     int end = e ? bit[pos] : 9;
    11     LL ans = 0;
    12     for (int i = 0; i <= end; i++)
    13     {
    14         ans += dfs(pos - 1, r, sum + i * (pos - r), e && (i == end));
    15     }
    16     if (!e) dp[pos][r][sum] = ans;
    17     return ans;
    18 }
    19 
    20 LL calc(LL n)
    21 {
    22     if (n < 0) return 0;
    23     int len = 0;
    24     while (n)
    25     {
    26         bit[len++] = n % 10;
    27         n /= 10;
    28     }
    29     LL ans = 0;
    30     for (int i = 0; i < len; i++)///需要枚举支点
    31         ans += dfs(len - 1, i, 0, 1);
    32     return ans - (len - 1);
    33 }
    34 int main ()
    35 {
    36     memset(dp, -1, sizeof(dp));
    37     int t;
    38     LL l, r;
    39     RI(t);
    40     while (t--)
    41     {
    42         scanf("%I64d%I64d", &l, &r);
    43         printf("%I64d
    ", calc(r) - calc(l - 1));
    44     }
    45 
    46     return 0;
    47 }
    View Code

    HDU4352严格上升子序列的长度为K的个数。!!!

    最长上升子序列结合,通过集合(1<<10)来处理

     1 LL dp[25][1 << 11][11];
     2 int bit[25];
     3 int k;
     4 int getnews(int s, int x)
     5 {
     6     for(int i=x;i<10;i++)
     7         if(s&(1<<i))return (s^(1<<i))|(1<<x);
     8     return s|(1<<x);
     9 }
    10 int getnum(int s)
    11 {
    12     int ret = 0;
    13     while (s)
    14     {
    15         if (s & 1) ret++;
    16         s >>= 1;
    17     }
    18     return ret;
    19 }
    20 LL dfs(int pos, int s, int e, int z)
    21 {
    22     if (pos == -1) return getnum(s) == k;
    23     if (!e && ~dp[pos][s][k]) return dp[pos][s][k];
    24     int end = e ? bit[pos] : 9;
    25     LL ans = 0;
    26 
    27     for (int i = 0; i <= end; i++)
    28     {
    29         int news = getnews(s, i);
    30         if (z && !i) news = 0;
    31         ans += dfs(pos - 1, news, e && (i == end), z && (!i));
    32     }
    33     if (!e) dp[pos][s][k] = ans;
    34     return ans;
    35 }
    36 
    37 LL calc(LL n)
    38 {
    39     int len = 0;
    40     while (n)
    41     {
    42         bit[len++] = n % 10;
    43         n /= 10;
    44     }
    45     return dfs(len - 1, 0, 1, 1);
    46 }
    47 
    48 int main ()
    49 {
    50     LL n, m;
    51     memset(dp, -1, sizeof(dp));
    52     int t;
    53     scanf("%d", &t);
    54     int nc = 1;
    55     while (t--)
    56     {
    57         cin >> n >> m >> k;
    58         printf("Case #%d: ", nc++);
    59         cout << calc(m) - calc(n - 1) << endl;
    60     }
    61     return 0;
    62 }
    View Code

    !!!是比较不错,待看的题

    比较还好的处理的题目:

    Codeforces 55D Beautiful numbers!!!

    spoj 10606 Balanced Numbers

    ac自动机和数位dp结合(!!!):

    hdu4376!!!(区间不可减???)
    ZOJ3494 BCD Code(AC自动机+数位DP)!!!

    整除和简单统计:

    HDU4507 和7无关数的平方和!!!

    HDU 3652 出现13,而且能被13整除

    LightOJ 1068 能被K整数且各位数字之和也能被K整除的数

    light OJ 1140两个数之间的所有数中零的个数。

    lightoj 1032  二进制数中连续两个‘1’出现次数的和

    其它:
    LightOJ1205求区间[a,b]的回文数个数。
    ural 1057 数位统计
    codeforces215E周期数
    codeforces258B在1-m中任选7个数,要使前六个数字中的“4”,"7"之和小于第七个的,
    Zoj2599 数位统计(见题意)
    zoj3162分形、自相似

  • 相关阅读:
    Maven导入com.google.common.collect jar包
    poj 2192 Zipper
    poj 3278 Catch That Cow
    poj 2488 A Knight's Journey
    poj 3982 序列
    poj 2109 Power of Cryptography
    poj 3258 3273
    java中大数的一些基本运算
    hdu 1715 大菲波数
    最小生成树模板
  • 原文地址:https://www.cnblogs.com/zyxStar/p/4563830.html
Copyright © 2011-2022 走看看