LeetCode---数位DP

简介

数位DP是一种计数的DP,一般就是要统计一个区间[l,r]内满足一些条件的个数。所谓数位DP, 意思就是在数位(个位,十位,百位,,,)上进行DP。

对于这种问题的求解思路:先得出解ans[x]([1,x]区间内满足条件的个数的方法,利用前缀和的思路,最终的解为ans[r] - ans[l-1]。

问题的关键是:怎么求ans[x]?

我们采用枚举的方式,控制上界枚举,从最高位开始往下枚举,例如: x = 213,那么我们从百位枚举,百位的可能情况是0,1,2,

当百位是1 的时候,十位和个位都是从0到9;当百位是2的时候,十位就从0到1。。。。总之不要枚举超过上界213就可以了。

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

给定一个正整数 n,找出小于或等于 n 的非负整数中,其二进制表示不包含 连续的1 的个数。

class Solution {
public:
    int a[111];
    int dp[33][2];

    int dfs(int pos,int state,bool limit)
    {
        if(pos == -1)return 1;
        if(!limit && dp[pos][state] != -1)return dp[pos][state];//未达到上限,记忆化
        int up = limit?a[pos]:1;
        int sum = 0;
        for(int i = 0;i<=up;++i)
        {
            if(state && i == 1)continue;
            sum += dfs(pos-1,i==1,limit && i == up);
        }
        if(!limit)dp[pos][state] = sum;//记忆化
        return sum;

    }
    int solve(int x)
    {
        int pos = 0;
        while(x)
        {
            a[pos++] = x&1;
            x >>= 1;
        }
        return dfs(pos-1,0,1);
    }
    int findIntegers(int num) {
        memset(dp,-1,sizeof(dp));
        return solve(num);
    }

    
};

例题---不要62 HDU - 2089 

题意:给定一个区间,求出“吉利数”的个数。吉利数:不含62或4

#include<bits/stdc++.h>

using namespace std;

int a[22];
int dp[22][2];

int dfs(int pos,int state,bool limit)
{
    if(pos == -1) return 1;
    if(!limit && dp[pos][state] != -1)return dp[pos][state];
    int up = limit?a[pos]:9;
    int sum = 0;
    for(int i = 0;i <= up;++i)
    {
        if(state && i == 2)continue;
        if(i == 4)continue;
        sum += dfs(pos-1,i==6,limit && i == up);
    }
    if(!limit)dp[pos][state] = sum;
    return sum;
}


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

int main()
{
    int l,r;
    while(cin>>l>>r && l + r)
    {
        memset(dp,-1,sizeof dp);
        cout << solve(r) - solve(l-1) << endl;
    }
}

例题---整数中1出现的次数(从1到n整数中1出现的次数)

leetcode233. 数字 1 的个数

class Solution {
public:

	int dp[20][20],num[20],cnt=0;

	int dfs(int pos,int flag,int sum){//sum表示pos位前面有几个1
		if(pos == -1)return sum;
		if(!flag && ~dp[pos][sum])return dp[pos][sum];//dp[pos][sum]表示到第pos位之前有sum个1的记忆搜索值是多少
		int up = flag? num[pos]:9,ans = 0;
		for(int i = 0;i<=up;++i){
			ans+=dfs(pos-1,flag && (i == up),sum+(i==1));
		}
		if(!flag)dp[pos][sum] = ans;
		return ans;
	}

	int cal(int x){
		while(x){
			num[cnt++]=x%10;
			x/=10;
		}
		return dfs(cnt-1,1,0);
	}
    int countDigitOne(int n) {
        if(!n)return 0;
        memset(dp,-1,sizeof dp);
        return cal(n);
    }
};



//数学方法,数学方法蛮好理解的,写个数字推一下就好了。
class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(n<1)return 0;
        long long ans = 0;
        long long round = n;
        long long base = 1;

        while(round){
        	int w = round % 10;
        	round /= 10;
        	ans += round * base;
        	if(w == 0)ans += 0;
        	else if(w == 1)ans += (n%base)+1;
        	else ans += base;
        	base *= 10;
        }
        return ans;
    }

};
发布了257 篇原创文章 · 获赞 36 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/sgh666666/article/details/105182323
今日推荐