数位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 }

其它一些题目:

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 }

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 }

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 }

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 }

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\n", (calc(b, 0) - calc(a, 1) + MOD) % MOD);
71     }
72     return 0;
73 }

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\n", calc(r) - calc(l - 1));
44     }
45
46     return 0;
47 }

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 }

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

比较还好的处理的题目:

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分形、自相似

时间: 2024-10-04 23:06:32

数位dp小记的相关文章

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

【HDU 3652】 B-number (数位DP)

B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task

hdu 5898 odd-even number 数位DP

odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 716    Accepted Submission(s): 385 Problem Description For a number,if the length of continuous odd digits is even and the length

LightOJ1068 Investigation(数位DP)

这题要求区间有多少个模K且各位数之和模K都等于0的数字. 注意到[1,231]这些数最大的各位数之和不会超过90左右,而如果K大于90那么模K的结果肯定不是0,因此K大于90就没有解. 考虑到数据规模,数据组数,这题状态这么表示: dp[i][j][k]:位数为i模K结果为j且各位数之和模K结果为k的数字个数 然后就是转移方程,最后就是统计.. 统计部分好棘手...半乱搞下AC的..还是对数位DP的这一部分太不熟悉了. 1 #include<cstdio> 2 #include<cstr

HDU 4734 F(x)(数位DP)

Description For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there betwe

hdu 4734 数位dp

http://acm.hdu.edu.cn/showproblem.php?pid=4734 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, plea