leetcode 552. Student Attendance Record II & 600. Non-negative Integers without Consecutive Ones

写在前面

两道动态规划的题目,题型类似,解法都类似LIS,但是都需要一些后续处理,因此放在一起整理。

leetcode 552. Student Attendance Record II

题目描述

Given a positive integer n, return the number of all possible attendance records with length n, which will be regarded as rewardable. The answer may be very large, return it after mod 109 + 7.

A student attendance record is a string that only contains the following three characters:

‘A’ : Absent.
‘L’ : Late.
‘P’ : Present.
A record is regarded as rewardable if it doesn’t contain more than one ‘A’ (absent) or more than two continuous ‘L’ (late).

Example 1:
Input: n = 2
Output: 8
Explanation:
There are 8 records with length 2 will be regarded as rewardable:
“PP” , “AP”, “PA”, “LP”, “PL”, “AL”, “LA”, “LL”
Only “AA” won’t be regarded as rewardable owing to more than one absent times.
Note: The value of n won’t exceed 100,000.

解题思路

简单解释一下题目,标题意思是:学生出席记录。给定一个正整数n,代表一个学生的出席记录的天数,出席记录满足一定规则的情况下,则该记录视作是可获得奖励的。规则如下:
P:代表当天出席
L:代表迟到
A:代表缺席
具体的规则如下:
1.记录中的A不能超过1个
2.连续的L不能超过2个
一个巧妙的解决思路:我们先不考虑A,因为A完全可以最后考虑(在已获得的序列中插入A即可)。这样余下的规则只剩一个:连续的L不超过两个。很明显,这是一个动态规划问题,连续的L不超过两个,我们只需要保证从i=2开始的序列中不含有超过两个的连续L,考虑序列结尾,我们只需要保证序列从i=2开始的尾部不含有超过连续的两个L即可。
我们以P[i] 表示第i个以P结尾的记录的个数,以PORL[i]表示第i位为P或L结尾的记录的个数(单纯的L也可以写出状态转移方程,不过事实上还是需要两者组合)。我们可以写出状态转移方程:

P[i] = PORL[i-1];
PORL[i] = P[i]+P[i-1]+P[i-2];

若为P,L 同样的状态转移方程为:
P[i] = P[i-1]+L[i-1];
L[i] = P[i-1]+P[i-2];
此时PORL[i] = P[i]+L[i];

得到上述状态转移方程后,我们很容易得到PORL的数组,分别代表某一位的所有可能情况,我们知道PORL[n]就是不包含A的情况下的所有可能性,我们在此数组中插入A,方法数就是在不同位置处插入A得到的总和。由此写出的代码如下:

class Solution {
public:
    int checkRecord(int n) {                                                                                                                
        // end with P,A,L
        int mod = 1000000007;
        vector<long> P(n+1,0);
        vector<long> PORL(n+1,0); // two 
        vector<long> L(n+1,0);
        P[0] = PORL[0] = 1;
        P[1] = 1;
        PORL[1] = 2;
        L[0] = 1;
        L[1] = 1;
        L[2] = 2;
        for(int i = 2;i<=n;++i) {

            P[i] = (P[i-1]+L[i-1])%mod;
            L[i] = (P[i-1]+P[i-2])%mod;
            PORL[i] = (P[i]+L[i])%mod;
            // P[i] = PORL[i-1];
            // PORL[i] = (P[i]+P[i-1]+P[i-2])%mod;
        }
        long ret = PORL[n];
        for(int i = 0;ilong t =(PORL[i]*PORL[n-i-1])%mod;
            ret = (ret+t)%mod;
        }
        return (int)ret;
    }
};

注释部分是第一种状态转移方程的表示,未注释部分是第二种状态转移方程的表达。

leetcode 600. Non-negative Integers without Consecutive Ones

题目描述

Given a positive integer n, find the number of non-negative integers less than or equal to n, whose binary representations do NOT contain consecutive ones.

Example 1:
Input: 5
Output: 5
Explanation:
Here are the non-negative integers <= 5 with their corresponding binary representations:
0 : 0
1 : 1
2 : 10
3 : 11
4 : 100
5 : 101
Among them, only integer 3 disobeys the rule (two consecutive ones) and the other 5 satisfy the rule.
Note: 1 <= n <= 10 9

思路分析

本题的要求是找出所有小于n的数中,二进制表示不包含连续1的数的个数。我们先不管小于n的条件,这样问题转化为,某个长度下不含连续1的所有可能情况,跟上题基本一样的思路,同样是动态规划,我们写出对应的状态转移方程:
d1[i] = d0[i-1];
d0[i] = d0[i-1]+d1[i-1];
d1[i]表示第i个数以1为结尾 d0[i] 表示第i个数以0为结尾,根据状态转移方程,可以得到固定长度下的满足情况的可能数的个数。
回到第一个限制条件,小于n。我们从最高位开始考虑,若n包含连续的1,那么很显然,剩下的所有数一定满足条件(因为d1[i]+d0[i]一定不含连续的1)。其次,若为连续的0,则意味着d1[i]必须被减掉,因为这样的情况下d1[i]中的所有数都比当前的数大(其实也是动态规划思想),而剩余的两种情况(不连续的1和0),都需要继续循环判断,根据以上的分析,写出的代码如下:

class Solution {
public:
    int findIntegers(int num) {
        // 连续的1的特点
        bitset<32> mBit(num);
        vector<int> mVec;
        bool isStarted = false;
        for(int i = 31;i>=0;--i) {
            if(mBit[i]||isStarted) 
            {
                isStarted = true;
                mVec.insert(mVec.begin(),mBit[i]);
            }
        }
        int n = mVec.size();
        vector<int> d0(n,0);
        vector<int> d1(n,0);
        d0[0] = 1;
        d1[0] = 1;
        for(int i = 1;i1];
            d0[i] = d0[i-1]+d1[i-1];
        }
        int count = d0[n-1]+d1[n-1];
        for(int i = n-2;i>=0;--i) {
            if(mVec[i]==1&&mVec[i+1]==1) break;
            if(mVec[i]==0&&mVec[i+1] == 0) count-=d1[i];
        }
        return count;
    }
};

使用bitset获取n的二进制表示。

你可能感兴趣的:(C++,leetcode,算法)