• 数位dp


    一,入门题

    例一:HDU 2089 不要62

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;

    int dp[21][3];
    int a[100];

    int dfs(int pos,int pre,int sta,bool limit){//limit表示是否受到限制,即这一位是否能到9,因为假如从第一位
    //开始就和枚举的数字一样,那么后面都不能大于该位,
        if(pos==-1) return 1;
        if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
        int up=limit?a[pos]:9;
        int ans=0;
        for (int i=0;i<=up;i++){
            if(pre==6&&i==2) continue;
            if(i==4) continue;
            ans+=dfs(pos-1,i,i==6,limit && i==a[pos]);
        }
        if(!limit) dp[pos][sta]=ans;//因为是受到限制的,且状态设计是该位是否是6,所以这里不能转移,
        return ans;
    }

    int solve(int x){
        int pos=0;
        while(x){
            a[pos++]=x%10;
            x/=10;
        }
        return dfs(pos-1,-1,0,true);
    }

    int main(){
        int l,r;
        memset(dp,-1,sizeof(dp));
        while(scanf("%d%d",&l,&r) && l+r ){
           // scanf("%d%d",&l,&r);
            printf("%d ",solve(r)-solve(l-1));
        }
    return 0;
    }

     二,

    第二:相减。
    例题:HDU 4734
    题目给了个f(x)的定义:F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,Ai是十进制数位,然后给出a,b求区间[0,b]内满足f(i)<=f(a)的i的个数。
    常规想:这个f(x)计算就和数位计算是一样的,就是加了权值,所以dp[pos][sum],这状态是基本的。a是题目给定的,f(a)是变化的不过f(a)最大好像是4600的样子。如果要memset优化就要加一维存f(a)的不同取值,那就是dp[10][4600][4600],这显然不合法。
    这个时候就要用减法了,dp[pos][sum],sum不是存当前枚举的数的前缀和(加权的),而是枚举到当前pos位,后面还需要凑sum的权值和的个数,
    也就是说初始的是时候sum是f(a),枚举一位就减去这一位在计算f(i)的权值,那么最后枚举完所有位 sum>=0时就是满足的,后面的位数凑足sum位就可以了。
    仔细想想这个状态是与f(a)无关的(新手似乎很难理解),一个状态只有在sum>=0时才满足,如果我们按常规的思想求f(i)的话,那么最后sum>=f(a)才是满足的条件。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int dp[12][10000+5];
    
    int all;
    int a[100];
    
    int f(int x){
        if(x==0) return 0;
        int ans=f(x/10);
        return ans*2+(x%10);
    }
    
    int dfs(int pos,int sum,bool limit){
        if(pos==-1) return sum<=all;
        if(sum>all) return 0;
        if(!limit && dp[pos][all-sum]!=-1) return dp[pos][all-sum];
        int up=limit?a[pos]:9;
        int ans=0;
        for (int i=0;i<=up;i++){
            ans+=dfs(pos-1,sum+i*(1<<pos),limit && i==up);
        }
        if(!limit) dp[pos][all-sum]=ans;//用减是因为,若我们设dp[pos][sum],表示到pos位置,的和,但是由于要加权,所以相同的dp[pos][sum]可能不一样,设成到答案的就科学了(
    其实这样设主要因为会超时,如果反复找)
    return ans; } int solve(int x){ int pos=0; while(x){ a[pos++]=x%10; x/=10; } return dfs(pos-1,0,true); } int main(){ int T; scanf("%d",&T); int a,b; memset(dp,-1,sizeof(dp)); for (int i=1;i<=T;i++){ scanf("%d%d",&a,&b); all=f(a); printf("Case #%d: %d ",i,solve(b)); } return 0; }
  • 相关阅读:
    计算机操作系统之进程管理
    剑指offer——两个链表的第一个公共结点
    剑指offer——数字在排序数组中出现的次数
    剑指offer——二叉树的深度与平衡二叉树的判断
    剑指offer——数组中只出现一次的数字
    剑指offer——和为s的两个数字VS和为s的连续正数序列
    剑指offer——翻转单词顺序VS左旋转字符串
    剑指offer——扑克牌的顺子
    剑指offer——圆圈中最后剩下的数字
    剑指offer——求1+2+...+n
  • 原文地址:https://www.cnblogs.com/lmjer/p/9799899.html
Copyright © 2020-2023  润新知