zoukankan      html  css  js  c++  java
  • 数位DP学习笔记

    现在让我来介绍一下数位DP......什么是数位DP呢qwq......数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp。

    数位DP其实也是一种记忆化搜索吧qwq,与其说是一种算法,不如说是一种思想qwq
    为了介绍它,我们这里先捞一个模板:(模板转自wust_wenhao)

         typedef long long ll;  
        int a[20];  
        ll dp[20][state];//不同题目状态不同  
        ll dfs(int pos,/*state变量*/,bool lead/*前导零*/,bool limit/*数位上界变量*/)//不是每个题都要判断前导零  
        {  
            //递归边界,既然是按位枚举,最低位是0,那么pos==-1说明这个数我枚举完了  
            if(pos==-1) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */  
            //第二个就是记忆化(在此前可能不同题目还能有一些剪枝)  
            if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];  
            /*常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应,具体为什么是有条件的记忆化后面会讲*/  
            int up=limit?a[pos]:9;//根据limit判断枚举的上界up;这个的例子前面用213讲过了  
            ll ans=0;  
            //开始计数  
            for(int i=0;i<=up;i++)//枚举,然后把不同情况的个数加到ans就可以了  
            {  
                if() ...  
                else if()...  
                ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos]) //最后两个变量传参都是这样写的  
                /*这里还算比较灵活,不过做几个题就觉得这里也是套路了 
                大概就是说,我当前数位枚举的数是i,然后根据题目的约束条件分类讨论 
                去计算不同情况下的个数,还有要根据state变量来保证i的合法性,比如题目 
                要求数位上不能有62连续出现,那么就是state就是要保存前一位pre,然后分类, 
                前一位如果是6那么这意味就不能是2,这里一定要保存枚举的这个数是合法*/  
            }  
            //计算完,记录状态  
            if(!limit && !lead) dp[pos][state]=ans;  
            /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/  
            return ans;  
        }  
        ll solve(ll x)  
        {  
            int pos=0;  
            while(x)//把数位都分解出来  
            {  
                a[pos++]=x%10;//个人老是喜欢编号为[0,pos),看不惯的就按自己习惯来,反正注意数位边界就行  
                x/=10;  
            }  
            return dfs(pos-1/*从最高位开始枚举*/,/*一系列状态 */,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛  
        }  
        int main()  
        {  
            ll le,ri;  
            while(~scanf("%lld%lld",&le,&ri))  
            {  
                printf("%lld
    ",solve(ri)-solve(le-1));  
            }  
        }  
    

    推荐题目:

    nowcoder提高组模拟赛 数数字
    BZOJ3679 数字之积
    CF1036C Classy Numbers
    ZJOI2010 数字计数
    luogu 萌数

    这里以一位dalao出的题来做示例吧:

    数据范围(1<=t<=10^5,0<=l_i<=r_i<=10^{18})

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define MAXN 20
    using namespace std;
    //0-->none
    //1-->0
    //2-->00
    //3-->007
    int t;
    long long ansans;
    long long cur[MAXN][MAXN],a[MAXN];
    inline long long search(int pos,int name,bool lead,bool limit)
    {
    	if(pos==0&&name==3) return 1;
    		else if(pos==0&&name!=3) return 0;
    	if(!limit&&!lead&&cur[pos][name]) return cur[pos][name];
    	int up=limit?a[pos]:9;
    	long long ans=0;
    	for(int i=0;i<=up;i++)
    	{
    		if(name==0) ans+=search(pos-1,(i==0&&!lead)?1:0,lead&&i==0,limit&&i==a[pos]);
    		else if(name==1) ans+=search(pos-1,(i==0&&!lead)?2:1,lead&&i==0,limit&&i==a[pos]);
    		else if(name==2) ans+=search(pos-1,i==7?3:2,lead&&i==0,limit&&i==a[pos]);
    		else ans+=search(pos-1,3,lead&&i==0,limit&&i==a[pos]);
    	}
    	if(!limit&&!lead) cur[pos][name]=ans;
    	return ans;
    }
    inline long long solve(long long x)
    {
    	memset(a,0,sizeof(a));
    	memset(cur,0,sizeof(cur));
    	int pos=0;
    	while(x)
    	{
    		a[++pos]=x%10;
    		x/=10;
    	}
    	return search(pos,0,true,true);
    }
    int main()
    {
    	freopen("friend.in","r",stdin);
    	freopen("friend.out","w",stdout);
    	scanf("%d",&t);
    	long long l,r;
    	while(t--)
    	{
    		scanf("%lld%lld",&l,&r);
    		ansans=ansans^(solve(r)-solve(l-1));
    	}
    	cout<<(long long)ansans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    1093 Count PAT's(25 分)
    1089 Insert or Merge(25 分)
    1088 Rational Arithmetic(20 分)
    1081 Rational Sum(20 分)
    1069 The Black Hole of Numbers(20 分)
    1059 Prime Factors(25 分)
    1050 String Subtraction (20)
    根据生日计算员工年龄
    动态获取当前日期和时间
    对计数结果进行4舍5入
  • 原文地址:https://www.cnblogs.com/fengxunling/p/9790161.html
Copyright © 2011-2022 走看看