【DP】数位DP
算法介绍
这是一类形式比较固定的DP问题,往往是求在某个区间 [A, B] 中满足特定条件数的个数。做法基本上是下面的一套模板。
// 计算 0 - x 内有多少满足条件的数
int dp(int x) {
// 0 特判
if(!x) return 0;
// 记录数字x的各位上的数字
vector<int> nums;
// B 为对应进制,通常是10进制下的问题
while(x) nums.push_back(x % B), x /= B;
// 最终答案
int res = 0;
// 上一位数的状态
int last = 0;
for(int i=nums.size()-1;i>=0;i--) {
int t = nums[i];
// do sth.
if(!i) {
// do sth.
}
}
return res;
}
通常,对于 i 位上的数字 k , [0, k-1
上的状态我们可以通过诸如区间DP方式求出来。
例题讲解
例题 | 做法描述 | 代码 |
---|---|---|
AcWing 1081. 度的数量 | 直接看代码 | 🔗 |
AcWing 1082. 数字游戏 | 直接看代码 | 🔗 |
AcWing 1083. Windy数 | 直接看代码 | 🔗 |
AcWing 1084. 数字游戏 II | 直接看代码 | 🔗 |
AcWing 1085. 不要62 | 直接看代码 | 🔗 |
AcWing 1081. 度的数量
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 32;
LL f[N][N];
int X, Y, K, B;
int dp(int x) {
if (!x) return 0;
vector<int> nums;
while (x) nums.push_back(x % B), x /= B;
int res = 0;
int last = 0;
for (int i = nums.size() - 1; i >= 0; i--) {
int t = nums[i];
if (t != 0) {
if (t > 1) {
res += f[i + 1][K - last];
break;
} else {
res += f[i][K - last];
last++;
if (last > K) break;
}
}
if (!i && last == K) res++;
}
return res;
}
int main() {
for (int i = 0; i < N; i++) {
for (int j = 0; j <= i; j++) {
if (j == 0) f[i][j] = 1;
else f[i][j] = f[i - 1][j - 1] + f[i - 1][j];
}
}
cin >> X >> Y >> K >> B;
cout << dp(Y) - dp(X - 1) << endl;
return 0;
}
####AcWing 1082. 数字游戏
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <deque>
#include <map>
using namespace std;
int a, b;
const int N = 12;
int f[N][N];
void init() {
for (int len = 1; len < N; len++) {
for (int i = 0; i <= 9; i++) {
if (len == 1) f[i][len] = 1;
else {
for (int j = i; j <= 9; j++) {
f[i][len] += f[j][len - 1];
}
}
}
}
}
int dp(int x) {
if (!x) return 1;
vector<int> nums;
while (x) nums.push_back(x % 10), x /= 10;
int res = 0, last = 0;
for (int i = nums.size() - 1; i >= 0; i--) {
int k = nums[i];
if (last > k) break;
for (int j = last; j < k; j++) {
res += f[j][i + 1];
}
last = k;
if (!i) res++;
}
return res;
}
int main() {
init();
// cout << f[2][2] << endl;
while (cin >> a >> b) {
cout << dp(b) - dp(a - 1) << endl;
}
return 0;
}
AcWing 1083. Windy数
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <deque>
#include <map>
using namespace std;
int a, b;
const int N = 12;
int f[N][N], g[N][N];
void init() {
for (int len = 1; len < N; len++) {
for (int i = 0; i <= 9; i++) {
if (len == 1) f[i][len] = 1, g[i][len] = 1;
else {
for (int j = 0; j <= 9; j++) {
if (abs(j - i) >= 2) {
f[i][len] += f[j][len - 1];
}
}
if (i == 0) {
for (int j = 0; j <= 9; j++) {
g[0][len] += g[j][len - 1];
}
} else {
g[i][len] = f[i][len];
}
}
}
}
}
int dp(int x) {
if (x >= 0 && x <= 9) return x + 1;
vector<int> nums;
while (x) nums.push_back(x % 10), x /= 10;
int res = 0, last = -2;
bool flag = false;
for (int i = nums.size() - 1; i >= 0; i--) {
int k = nums[i];
if(k != 0) {
if (!flag) {
res += g[0][i + 1];
} else if (abs(last) >= 2) {
res += f[0][i + 1];
}
for (int j = 1; j < k; j++) {
if (abs(last - j) >= 2) {
res += f[j][i + 1];
}
}
}
if (abs(last - k) <= 1) break;
last = k;
if (k != 0) flag = true;
if (!i) res++;
}
return res;
}
int main() {
init();
cin >> a >> b;
cout << dp(b) - dp(a - 1) << endl;
return 0;
}
####AcWing 1084. 数字游戏 II
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <deque>
#include <map>
using namespace std;
int a, b;
const int N = 12;
int f[N][N][105];
int mod;
void init() {
for (int len = 1; len < N; len++) {
for (int i = 0; i <= 9; i++) {
if (len == 1) {
f[i][len][i % mod] = 1;
continue;
}
for (int j = 0; j < mod; j++) {
for (int k = 0; k <= 9; k++) {
f[i][len][j] += f[k][len - 1][((j - i) % mod + mod) % mod];
}
}
}
}
}
int dp(int n) {
if (!n) return 1;
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0, last = 0;
for (int i = nums.size() - 1; i >= 0; i--) {
int k = nums[i];
for (int j = 0; j < k; j++) {
res += f[j][i + 1][((mod - last) % mod + mod) % mod];
}
last = (last + k) % mod;
if (!n && last % mod == 0) res++;
}
return res;
}
int main() {
while (cin >> a >> b >> mod) {
memset(f, 0, sizeof(f));
init();
cout << dp(b) - dp(a - 1) << endl;
}
return 0;
}
####AcWing 1085. 不要62
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 12;
int f[N][N];
void init() {
for (int len = 1; len < N; len++) {
for (int i = 0; i <= 9; i++) {
if (i == 4) continue;
if (len == 1) f[i][len] = 1;
else {
if (i == 6)
for (int j = 0; j <= 9; j++) {
if (j == 2 || j== 4) continue;
f[i][len] += f[j][len - 1];
}
else {
for (int j = 0; j <= 9; j++) {
if(j == 4) continue;
f[i][len] += f[j][len - 1];
}
}
}
}
}
}
int n, m;
int dp(int x) {
if (!x) return 1;
vector<int> nums;
while (x) nums.push_back(x % 10), x /= 10;
int last = 0, res = 0;
// res += f[0][nums.size()];
for (int i = nums.size() - 1; i >= 0; i--) {
int t = nums[i];
for (int j = 0; j < t; j++) {
// if(i == nums.size() - 1&& j == 0) continue;
if(last == 6 && j == 2) continue;
if(j == 4) continue;
res += f[j][i+1];
}
if(t == 4) break;
if(last == 6 && t == 2) break;
last = t;
if(!i) res++;
}
return res;
}
int main() {
init();
while (cin >> n >> m, !(n == 0 && m == 0)) {
cout << dp(m) - dp(n - 1) << endl;
}
return 0;
}