区间、计数、数位统计、状态压缩、树形等DP问题 发表于 2025-08-05 更新于 2025-08-24
字数总计: 1.3k 阅读时长: 6分钟 阅读量: 济宁
数据结构 算法 区间、计数、数位统计、状态压缩、树形等DP问题 wyp 2025-08-05 2025-08-24 区间DP AcWing 282. 石子合并 计数类DP AcWing 900. 整数划分 数位统计DP AcWing 338. 计数问题
数字
1
11
21
31
41
51
61
71
81
91
101
111
231
991
ans
1
2
3
4
5
6
7
8
9
10
11
12
24
100
发现一个规律:假设x的每一位分别为abcdefg,那么当g == 1时,我们的答案就是abcdef + 1
假如g < 1的,那最后就不会统计到abcdef1这个数字,答案就是abcdefg
如果g > 1,那么答案也是abcdef + 1
所以统计个位数的规律我们可以视为 n/10+(n%10>=1)
x
100
200
300
1000
1600
161a(9=>a>=0)
1650
n
10
20
30
100
160
160+(a+1)
160+10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <bits/stdc++.h> using namespace std;const int N = 10 ;int get (vector<int > num, int l, int r) { int res = 0 ; for (int i = l; i >= r; i -- ) res = res * 10 + num[i]; return res; } int power10 (int x) { int res = 1 ; while (x -- ) res *= 10 ; return res; } int count (int n, int x) { if (!n) return 0 ; vector<int > num; while (n) { num.push_back (n % 10 ); n /= 10 ; } n = num.size (); int res = 0 ; for (int i = n - 1 - !x; i >= 0 ; i -- ) { if (i < n - 1 ) { res += get (num, n - 1 , i + 1 ) * power10 (i); if (!x) res -= power10 (i); } if (num[i] == x) res += get (num, i - 1 , 0 ) + 1 ; else if (num[i] > x) res += power10 (i); } return res; } int main () { int a, b; while (cin >> a >> b, a) { if (a > b) swap (a, b); for (int i = 0 ; i <= 9 ; i ++ ) cout << count (b, i) - count (a - 1 , i) << ' ' ; cout << endl; } return 0 ; }
这个使用了类似前缀和的方式 ,求a-b之间的数字,我们找到计算的规律后,直接用(0,b)-(0,a- 1)的,就得到答案了
状态压缩DP AcWing 291. 蒙德里安的梦想 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> using namespace std;const int N = 12 , M = 1 << N;int n, m;long long f[N][M]; bool st[M];int main () { while (cin >> n >> m, n || m) { for (int i = 0 ; i < 1 << n; i ++ ) { int cnt = 0 ; st[i] = true ; for (int j = 0 ; j < n; j ++ ) if (i >> j & 1 ) { if (cnt & 1 ) st[i] = false ; cnt = 0 ; } else cnt ++ ; if (cnt & 1 ) st[i] = false ; } memset (f, 0 , sizeof f); f[0 ][0 ] = 1 ; for (int i = 1 ; i <= m; i ++) for (int j = 0 ; j < 1 << n; j ++ ) for (int k = 0 ; k < 1 << n; k ++ ) if ((j & k) == 0 && st[j | k]) f[i][j] += f[i - 1 ][k]; cout << f[m][0 ] << endl; } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <bits/stdc++.h> using namespace std;typedef long long LL;const int N = 12 , M = 1 << N;int n, m;LL f[N][M]; vector<int > state[M]; bool st[M]; int main () { while (cin >> n >> m, n || m) { for (int i = 0 ; i < 1 << n; i ++ ) { int cnt = 0 ; bool is_valid = true ; for (int j = 0 ; j < n; j ++ ) if (i >> j & 1 ) { if (cnt & 1 ) { is_valid = false ; break ; } } else cnt ++ ; if (cnt & 1 ) is_valid = false ; st[i] = is_valid; } for (int i = 0 ; i < 1 << n; i ++ ) { state[i].clear (); for (int j = 0 ; j < 1 << n; j ++ ) if ((i & j) == 0 && st[i | j]) state[i].push_back (j); } memset (f, 0 , sizeof f); f[0 ][0 ] = 1 ; for (int i = 1 ; i <= m; i ++ ) for (int j = 0 ; j < 1 << n; j ++ ) for (auto k : state[j]) f[i][j] += f[i - 1 ][k]; cout << f[m][0 ] << endl; } return 0 ; }
AcWing 91. 最短Hamilton路径 树形DP AcWing 285. 没有上司的舞会 记忆化搜索 AcWing 901. 滑雪