关于此题P4124 [CQOI2016] 手机号码 [数位DP]的一些总结
传送门
数位DP都长这样吗(bushi
记忆化的维数一定得是能完全确定当前所在状态的,如果当前所在状态不能够由记忆化的各个维度共同确定的,即同一各个维度的值可能对应多个对答案有着不同贡献的状态的话,这个记忆化就会出问题(就相当于DP的维数并不能完整反应转移过程的各个状态一样
就拿这道题举例,在我的写法中,枚举到当前位时,需要六个量来完全确定当前状态,当前所在位数、目前为止是否出现4、目前为止是否出现8、前面枚举的所有位数当中出现连续相同数字最多多少次、上一位数是多少,以及包含当前位的出现连续相同数字多少个,漏一个都无法完整确定当前状态。
然而比如说这道题:P4999 烦人的数学作业,我们DP数组的状态只需两维,分别是当前所在位以及在此之前枚举的所有数的和,这就足够完全确定当前所在状态了,因为在此之前枚举的所有数的和如果都是sum的话,那么不管其各个位具体数字是多少,它们对答案的贡献都是相同的。
#include<bits/stdc++.h>
using namespace std;
long long t;
long long l,r;
long long dp[20][2][2][20][10][20];
vector<long long> digit;
long long dfs(long long pos,long long cot,bool v1,bool v2,bool done,long long nct,long long status,bool lead) {
if(pos < 0) {
if(cot >= 3 && (!v1 || !v2)) return 1;
else return 0;
}
if(!done && ~dp[pos][v1][v2][nct][status][cot] && !lead) return dp[pos][v1][v2][nct][status][cot];
long long res = 0,end = (done ? digit[pos] : 9);
for(long long i = 0;i <= end;i++) {
long long tmp = nct;
if(lead && !i) tmp = 0;
else if(i == status) tmp++;
else tmp = 1;
res += dfs(pos - 1,max(cot,tmp),v1 || (i == 4),v2 || (i == 8),done && (i == end),tmp,i,lead && !i);
}
if(!done && !lead) dp[pos][v1][v2][nct][status][cot] = res;
return res;
}
long long work(long long k) {
memset(dp,-1,sizeof dp);
digit.clear();
while(k) {
digit.push_back(k % 10);
k /= 10;
}
return dfs(digit.size() - 1,0,0,0,1,0,0,1);
}
void solve() {
cin >> l >> r;
cout << work(r) - work(l - 1);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
t = 1;
while(t--) solve();
return 0;
}