HDU3652 B-number (数位dp)

对于数位dp,很多人有着不同的板子,我也曾经是使用递推板子,后来发现那种方法太过于灵活,对预处理的思维要求很高,因此在这里讲解一下我这种dfs的板子的通法

首先最简单的板子是f[][][],表示处理到了前i位,前缀的状态是什么,以及是不是当前位还是受限于最高位

受限的意思是,比如12345,当我们处理到第5位是,前面四位是1234,这样我们最高只能到5,否则的话到9都是可以的

这是基本上每道题都要有的三个状态,对于这道题,显然我们还需要两个状态,一个表示前面的数模13的答案是多少,一个表示前面是否已经存在13的情况了

当递归的结束的时候,我们只需要判断模数是否为0且前面是否已经存在13这种情况即可。这题的前缀肯定是前面是哪个数

这样就能记忆化搜索了,本题不需要处理前导0,但是有一些题需要处理前导0,我们只需要再给一维状态表示是否还在前导0的数,之后在循环中特判一下就行

虽然状态看上去很多,但是很多都是2位,并不占多少空间

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int f[20][10][14][2][2];
string s;
string r;
int len;
int sum=13;
int dfs(int cur,int p1,int r,int m,int flag){
    if(cur==len)
        return m&&(r==0);
    auto &x=f[cur][p1][r][m][flag];
    if(x!=-1)
        return x;
    int v=9;
    if(flag)
        v=s[cur]-'0';
    int ans=0;
    int i;
    for(i=0;i<=v;i++){
        ans+=dfs(cur+1,i,(10*r+i)%sum,m||(i==3&&p1==1),flag&&(i==v));
    }
    return x=ans;
}
int solve(string t){
    s=t;
    len=t.size();
    int i;
    memset(f,-1,sizeof f);
    return dfs(0,0,0,0,1);
}
int main(){
    while(cin>>r){
        int ans=0;
        ans=solve(r);
        cout<<ans<<endl;
    }
    return 0;
}
View Code

 

posted @ 2020-04-15 15:55  朝暮不思  阅读(137)  评论(0编辑  收藏  举报