zoukankan      html  css  js  c++  java
  • # Acwing 1082 数字游戏

    Acwing 1082 数字游戏 [数位DP详解+模板]

    数位 DP 问题往往都是这样的题型,给定一个闭区间([L,R]),让你求这个区间中满足某种条件的数的总数。

    前缀和思想,转化为(f([0,R])-f([0,L-1]))求解。

    转化成求(f(N)),将上限N转化成10进制(根据题意转化为K进制,一般是十进制),枚举从最高位开始枚举N的10进制的每一位,只要该位的取值小于N的10进制对应位的取值,那么这个数必然小于等于N。

    一般使用DFS求解。

    inline int dfs(int pos,int pre,bool limit,bool lead)一般要记录当前在处理哪一位,当前位前一位的相关信息,当前位是否有上限限制,当前位是否有前导0限制(最高位不能取0)

    //limit向下一位传递的条件
    limit==1&&i==a[pos] //当前位有限制,并且当前位取到了限制下的最大值
    
    //lead向下一位传递的条件
    lead==1&&i==0 //当前位有前导0限制(当前位不能取0),并且当前位取的就是0
    

    例题 Acwing 1082 数字游戏

    题意

    求[X,Y]中的数,满足数字从左到右非下降关系的数的个数,如:123,445.

    思路

    数位dp经典题。

    前缀和思想,转化为(f([0,R])-f([0,L-1]))求解。

    转化成求(f(N)),将上限N转化成10进制(根据题意转化为K进制,一般是十进制),枚举从最高位开始枚举N的10进制的每一位,只要该位的取值小于N的10进制对应位的取值,那么这个数必然小于等于N。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    //f[i][j]表示前i位,第i+1位为j时的满足条件的数的总数
    //同时充当标记数组,DFS使用记忆化搜索,一般将f[][]初始化为-1,递归过程中如果出现f[i][j]!=-1,表示这个状态已经搜索过,直接返回答案
    //本题只需要记录一个值(数的个数),如果一个状态需要记录多个值,f数组定义为结构体
    int f[12][12];
    
    int a[12];//用于存储上限N的K进制下的每个数
    
    inline int dfs(int pos,int pre,bool limit){//pos表示当前处理的位,per记录前一位的信息,这里是记录前一位的值,limit表示当前位是否有上限的限制
        if(!pos)return 1;//递归出口
        
        if(!limit&&f[pos][pre]!=-1)return f[pos][pre];//当前位没有限制,并且已经搜索过,直接返回答案
    
        int up=limit?a[pos]:9;//获取当前位的取值上限,如果有限制,上限为n的对应位的值,否则上限为9
       
        int res=0;
        for(int i=0;i<=up;++i){//枚举当前位的取值
            if(i<pre)continue;//由于要满足不下降数的限制,如果当前位的取值比上一位小,当前位取值不合法,没有必要处理当前位取i时后面位的取值
            
            //当前位取值合法,递归枚举下一位的取值,下一位上限有限制的条件是limit==1&&a==a[pos](也就是说当前位有限制,并且当前位取了上限值。
            //比如123,百位取1,百位首先肯定是有限制的,因为百位最大取1,此时该位也取到了上限值1,那么下一位十位也是有限制的,不能超过2)。而如果当前位取0,那么十位就能随便取值了0-9都能取。
            res+=dfs(pos-1,i,limit&&i==a[pos]);
        }
        
        //记忆化搜索,只有在当前状态没有任何限制的时候(没有最高位限制和前导0限制),才表示这个状态可以通用。
        //return (limit||lead)?res:f[pos][pre]=res;
        return limit?res:f[pos][pre]=res;
    }
    
    inline int sol(int n){
        if(!n)return 1;//特殊处理n==0的情况
        
        memset(f,-1,sizeof f);//记忆化数组兼标记数组初始化为-1,有些状态可能本来就是0,所以初始化为-1
    
        //获取n的10进制下的每一位
        int pos=0;
        while (n)a[++pos]=n%10,n/=10;
    
        return dfs(pos,-1,true);
    }
    int main(){
        int l,r;
        while (cin>>l>>r){
            cout<<sol(r)-sol(l-1)<<endl;//转化为前缀和求解
        }
        return 0;
    }
    
  • 相关阅读:
    火车进出栈问题(卡特兰数)
    HDU 4699 Editor (对顶栈)
    HDU 6430 TeaTree (线段树合并)
    Exam 4895 Crowd Control
    Exam 4894 Booming Business
    8377: Playoff
    hdu 6345 Problem J. CSGO
    HDU 6437 Problem L.Videos
    Making the Grade
    poj2279——Mr. Young's Picture Permutations
  • 原文地址:https://www.cnblogs.com/sstealer/p/13298912.html
Copyright © 2011-2022 走看看