leetcode 数位dp

9 篇文章 0 订阅

绝了,又发现我之前数位dp板子是错的。。写了那么久才发现,之前样例太水了。

233.数字1的个数

题目
在这里插入图片描述

class Solution {
public:
    struct node{
        int cnt,sum;
        node(){sum=-1;}
        node(int cnt,int sum){this->cnt=cnt,this->sum=sum;}
    }dp[22];
    int a[22];
    node num_dp(int pos,int limit){
        if(!pos) return (node){1,0};
        if(!limit&&dp[pos].sum!=-1) return dp[pos];
        int up=limit?a[pos]:9;
        node ans={0,0};
        for(int i=0;i<=up;++i){
            node cur=num_dp(pos-1,limit&&(i==a[pos]));
            ans.cnt+=cur.cnt,ans.sum+=cur.sum;
            if(i==1) ans.sum+=cur.cnt; //第pos位等于1的时候
        }
        if(!limit) dp[pos]=ans;
        return ans;
    }
    int flag=0;
    int countDigitOne(int n) {
        if(!flag){//多次调用同一个对象的这个函数,数位dp才有意义,只调用一次dp白记忆化了
            int o=1e7,p=0;
            while(o) a[++p]=o%10,o/=10;
            num_dp(p,1);
            flag=1;
        }
        int p=0;
        while(n) a[++p]=n%10,n/=10;
        node ans=num_dp(p,1);
        return ans.sum;
    }
};

600. 不含连续1的非负整数

题目

在这里插入图片描述

class Solution {
public:
    int a[34],dp[34][2];
    int num_dp(int pos,int pre,int limit){
        if(!pos) return 1;
        if(!limit&&dp[pos][pre]!=-1) return dp[pos][pre];
        int up=limit?a[pos]:1;
        int ans=0;
        for(int i=0;i<=up;++i)
            if(!pre||i!=1) ans+=num_dp(pos-1,(i==1),limit&&(i==a[pos]));
        if(!limit) dp[pos][pre]=ans;
        return ans;
    }
    int flag;
    void init(){
        int o=1e9,p=0;
        while(o) a[++p]=o%2,o/=2;
        num_dp(p,0,1);
    }
    int findIntegers(int n) {
        if(!flag) {
            memset(dp,-1,sizeof dp);
            init(),flag=1;
        }
        int p=0;
        while(n) a[++p]=n%2,n/=2;
        return num_dp(p,0,1);
    }
};

357. 计算各个位数不同的数字个数(处理前导0)

题目
在这里插入图片描述
注意前面的位数都是0的话也就是有前导0,前面0其实都不算被使用。因为000123实际就是123.所以这题要处理前导0

class Solution {
public:
    int dp[22][(1<<10)+10],a[22];//因为是111111111 共9个1 所以不能(1<<9)+10
    int num_dp(int pos,int pre,int lead,int limit){
        if(!pos) return 1;
        if(!limit&&!lead&&dp[pos][pre]!=-1) return dp[pos][pre];
        int up=limit?a[pos]:9,ans=0;
        for(int i=0;i<=up;++i){
            if(lead&&(!i)) ans+=num_dp(pos-1,0,1,limit&&(i==a[pos]));//前面都是0 该位还是0 这时候0并不算被使用
            else{
                if(pre&(1<<i)) continue;
                ans+=num_dp(pos-1,pre|(1<<i),lead&&(!i),limit&&(i==a[pos]));
            }
        }
        if(!limit&&!lead) dp[pos][pre]=ans;
        return ans;
    }
    int flag;
    void init(){
        int o=1e8,p=0;
        while(o) a[++p]=o%10,o/=10;
        num_dp(p,0,1,1);
    }
    int countNumbersWithUniqueDigits(int n) {
        if(!flag){
            memset(dp,-1,sizeof dp);
            init(),flag=1;
        }
        n=pow(10,n),--n;//[0,10^n) 开区间,,何必呢
        int p=0;
        while(n) a[++p]=n%10,n/=10;
        return num_dp(p,0,1,1);
    }
};

902. 最大为 N 的数字组合(处理前导0)

题目
在这里插入图片描述
有序集合中不含0,但00035又是合法的,要处理前导0.

class Solution {
public:
    int dp[25],a[25];
    int num_dp(int pos,int lead,int limit,vector<int>&d){
        if(!pos) return 1;
        if(!limit&&!lead&&dp[pos]!=-1) return dp[pos];
        int ans=0;
        for(int i=0;i<d.size();++i){
            if(limit&&a[pos]<d[i]) break;
            ans+=num_dp(pos-1,0,limit&&(d[i]==a[pos]),d);
        }
        if(lead) ans+=num_dp(pos-1,1,limit&&(0==a[pos]),d);//假如不想算0 可以if(head&&pos!=1)
        if(!limit&&!lead) dp[pos]=ans;
        return ans;
    }
    int cal(string s){
        int ans=0;
        for(char c:s) ans=ans*10+(c-'0');
        return ans;
    }
    int atMostNGivenDigitSet(vector<string>& digits, int n) {
        memset(dp,-1,sizeof dp);
        vector<int> d;
        for(int i=0;i<digits.size();++i) d.push_back(cal(digits[i]));
        int p=0;
        while(n) a[++p]=n%10,n/=10;
        return num_dp(p,1,1,d)-1;//把0算上去了
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值