LeetCode 600. 不含连续1的非负整数--数位DP--动态规划

本文介绍了一个编程题目,目标是计算小于等于给定正整数n的二进制表示中不包含连续1的非负整数个数。通过动态规划和字符串处理技巧,博主提供了详细的解题思路和AC代码示例,适合理解二进制和动态规划在解决此类问题中的应用。
摘要由CSDN通过智能技术生成
  1. 不含连续1的非负整数

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

示例 1:

输入: 5
输出: 5
解释:
下面是带有相应二进制表示的非负整数<= 5:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
其中,只有整数3违反规则(有两个连续的1),其他5个满足规则。

说明: 1 <= n <= 109

题解

我们先把问题简化,因为涉及二进制数,所以转换成二进制数来考虑,于是比如长度为len(x)+2的二进制数,那么不符合要求的二进制数有:10x和11x,10x这种情况的数目为x中不符合要求的数目,我们假设coun[x]表示二进制长度为x的数字不符合要求的数目,那么10x的答案为coun[x],11x的结果就简单了,直接为pow(2,len(x)),因为前面的11已经不符合要求,后面的x无论怎么取都是不对的,于是我们分别dp[n][0]和dp[n][1]表示长度为n的二进制数10x和11x的数目分别为多少。

于是某个数字拆成二进制数为:110101,可以看成是:
100000 -> 5个位置随便取值,错误数+coun[5]
10000 -> 4个位置随便取值,错误数+coun[4]
100 -> 2个位置随便取值,错误数+coun[2]
1 -> 0个位置随便取值,错误数+coun[2]

分别对应,然后又回到整体,110101前面的11是不符合要求的,导致后面的101都是错的,101对应了6个数字(000也是一种情况),所以错误数加上这个部分。

最后就是110101代表的数字+1减去错误数为答案。

AC代码

class Solution 
{
public:
    int dp[33][2];
    int coun[33];
    string int_to_str(int x)
    {
        vector<int>q;
        while(x>0)
        {
            q.push_back(x%2);
            x/=2;
        }
        string t="";
        for(int i=q.size()-1;i>=0;i--)
        t+=(q[i]+'0');
        return t;
    }
    int findIntegers(int n) 
    {
        memset(dp,0,sizeof(dp));
        memset(coun,0,sizeof(coun));
        dp[0][0]=dp[0][1]=0;
        dp[1][0]=dp[1][1]=0;
        dp[2][0]=0;
        dp[2][1]=1;
        coun[2]=1;
        string num = int_to_str(n);
        for(int i=3;i<=num.length();i++)
        {
            dp[i][0] = coun[i-2];
            dp[i][1] = int(pow(2,i-2));
            coun[i] = coun[i-1] + dp[i][0] + dp[i][1];
            //cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<" "<<coun[i]<<endl;
        }
        //cout<<num<<endl;
        int res = coun[num.length()-1];
        for(int i=1;i<num.length();i++)
        {
            if(num[i]=='1')
            res += coun[num.length()-i-1];
            if(num[i]=='1'&&num[i-1]=='1')//特判
            {
                int sum=0;
                for(int j=num.length()-1,k=0;j>i;j--,k++)
                {
                    sum += (num[j]-'0')*int(pow(2,k));
                }
                sum++;//全0的情况
                res += sum;
                return n-res+1;
            }
        }
        return n-res+1;
    }
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值