数位DP及模板

数位DP:

一般来说数位DP有两种写法:1.for循坏枚举DP 2.记忆化搜索+DP

这里详细将记忆化搜索+DP

DFS状态:

常见的dfs状态有三个:

1.枚举到第几位(POS)

2.判断前面是否紧贴上限(LIMIT)

3.前面的数是否全为前导0 (ZERO)

 

这三个状态如何转移到下一位呢?

1.pos→pos+1

2.limit→limit&&a[i]=当前这位最高位

3.zero→zero&&a[i]=0

 

重点:dfs的核心是深搜,一搜搜到底,搜到底了,代表我们搜完了一个数,return 1;

 

 

 

现在问有多少的数比12345小

1.关于前导0:00999→999,09999→9999

2.关于limit前面的数是否紧贴上限

如果前面的数是紧贴上限的,当前这位枚举的上限便是当前数的上限

如果前面的数不是紧贴上限的,当前这位枚举的上限便是 9

3.关于DP维度

一般来说,DFS有几个状态,DP就几个维度  

比如现在DP就是DP [pos] [limt] [zero] 

4.关于记忆化DP

现在枚举到了 10××× 和 11×××

显然 这两种状态后面的×××状态数是一样的

重点:dp[pos][limit][zero]表示前面的数枚举状态确定,后面的数有多少种可能

5.关于DP细节

一般来说我们一开始都memset(dp,-1,sizeof(dp))

如果dp[pos][limt][zero]!=-1 return dp[pos][limit][zero];

6.关于初始化:

一开始 limit 是1,表示一开始的数只能选 1~a[1]

一开始zero 是1,假定表示前面的数全为0

 

最后放个数位DP模板:(询问 L~R 之间有多少的数)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   //vector函数
#define popb pop_back  //vector函数
#define fi first
#define se second
const int N=20;
//const int M=;
//const int inf=0x3f3f3f3f;     //一般为int赋最大值,不用于memset中
//const ll INF=0x3ffffffffffff; //一般为ll赋最大值,不用于memset中
int T,n,len,a[N],dp[N][2][2];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int dfs(int pos,bool lim,bool zero)
{
    if(pos>len) return 1;
    if(dp[pos][lim][zero]!=-1) return dp[pos][lim][zero];
    int res=0,num=lim?a[pos]:9;
    for(int i=0;i<=num;i++)
        res+=dfs(pos+1,lim && i==num,zero && i==0 );
    return dp[pos][lim][zero]=res; 
} 
int solve(int x)
{
    len=0;
    memset(dp,-1,sizeof(dp));
    for(;x;x/=10) a[++len]=x%10;
    reverse(a+1,a+len+1);
    return dfs(1,1,1);
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);    
    int l=read(),r=read();
    printf("%d\n",solve(r)-solve(l-1));
    return 0;
}

 还有另外一种形式的记忆化,这种是dp状态中少了 Limit 和Zero ,但是相应的速度可能远不如上面一个,但适合多组数据:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   
#define popb pop_back  
#define fi first
#define se second
#define popcount __builtin_popcount
const int N=10;
const int M=70;
//const int inf=0x3f3f3f3f;     
//const ll INF=0x3ffffffffffff;
int T,n,b,len,a[M];
ll dp[12][70][2000];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
ll dfs(int b,int pos,bool lim,bool zero,int status)
{
    if(!pos)
    {
//        printf("%d %d %d\n",pos,status,zero);
        if(zero) return 0;
        if(!status) return 1;
        else return 0;
    }
    if(!lim&&!zero&&dp[b][pos][status]!=-1) return dp[b][pos][status];
    int num=lim?a[pos]:b-1;
    ll res=0;
    for(int i=0;i<=num;i++)
    {
        if(zero&&!i) res+=dfs(b,pos-1,lim&&i==num,zero&&!i,status);
        else res+=dfs(b,pos-1,lim&&i==num,zero&&!i,status^(1<<i));
    }
    if(!lim&&!zero) dp[b][pos][status]=res;
    return res;
}
ll solve(ll x)
{
    len=0;
//    memset(dp,-1,sizeof(dp));
    for(;x;x/=b) a[++len]=x%b;
//    reverse(a+1,a+len+1);
    return dfs(b,len,1,1,0);
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
//    ios::sync_with_stdio(0);
//    cin.tie(0);
    for(int i=2;i<=10;i++) memset(dp,-1,sizeof(dp));
    T=read();
    while(T--)
    {
        b=read();
        ll l,r;
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}

 

posted @ 2023-01-24 15:46  QAQ啥也不会  阅读(179)  评论(0编辑  收藏  举报