Greedy algorithm: monotonically increasing numbers

Changed to a new typography theme, haha, how do you feel?

Notice: Some recording friends said that they often don’t see daily articles. Now the official account is not recommended according to the sending time, but pushes out of order according to some rules, so you may have followed the "Code Random Record" and never saw the article. It is recommended to set a star for "Code Caprice". After setting the star, it will be pushed according to the posting time every day. Carl sends it regularly at 8:35 every day, oh punctual!

738. Monotonically increasing numbers
Given a non-negative integer N, find the largest integer less than or equal to N, and this integer needs to satisfy that the numbers on each digit are monotonically increasing.

(If and only if the numbers x and y of each adjacent digit satisfy x <= y, we call this integer monotonically increasing.)

Example 1:
Input: N = 10
Output: 9

Example 2:
Input: N = 1234
Output: 1234

Example 3:
Input: N = 332
Output: 299

Explanation: N is an integer in the range of [0, 10^9].

The idea of
violent solution
is very simple, so the first thing to think about is violent solution. Let me mention a wave of violence. The result is naturally overtime!

code show as below:


class Solution {
private:
    bool checkNum(int num) {
        int max = 10;
        while (num) {
            int t = num % 10;
            if (max >= t) max = t;
            else return false;
            num = num / 10;
        }
        return true;
    }
public:
    int monotoneIncreasingDigits(int N) {
        for (int i = N; i > 0; i--) {
            if (checkNum(i)) return i;
        }
        return 0;
    }
};
  • Time complexity: O(n * m) m is the number length of n
  • Space complexity: O(1)
    Greedy algorithm
    problem requires the largest monotonically increasing integer less than or equal to N, so take a two-digit number as an example.

For example: 98, once strNum[i-1]> strNum[i] occurs (not monotonically increasing), first I want strNum[i-1]--, and then strNum[i] is 9 so that this integer is 89, which is the largest monotonically increasing integer less than 98.

If you think about this clearly, this question will be easier to handle.

Local optimal: In the case of strNum[i-1]> strNum[i], let strNum[i-1]--, and then strNum[i] to 9 to ensure that these two bits become the largest monotonically increasing integer .

Global optimal: get the largest monotonically increasing integer less than or equal to N.

But here the local optimum is derived from the global optimum, and other conditions are needed, that is, the traversal order, and the mark from which bit is changed to 9.

Is it to traverse from front to back or from back to front?

If you traverse from front to back, if strNum[i-1]> strNum[i] is encountered, let strNum[i-1] subtract one, but if strNum[i-1] is subtracted by one, it may be less than strNum [i-2].

This is a bit abstract, for example, the number: 332, if you traverse from front to back, then it becomes 329. At this time, 2 is smaller than the first 3, and the real result should be 299.

So traversing from front to back will change the results that have been traversed!

Then traversing from back to front, you can reuse the result of the last comparison. The value of 332 from back to front traversal changes to: 332 -> 329 -> 299

After determining the traversal order, then the local optimal can be introduced to the global at this time, and no counterexamples can be found, try to be greedy.

The C++ code is as follows:


class Solution {
public:
    int monotoneIncreasingDigits(int N) {
        string strNum = to_string(N);
        // flag用来标记赋值9从哪里开始
        // 设置为这个默认值,为了防止第二个for循环在flag没有被赋值的情况下执行
        int flag = strNum.size();
        for (int i = strNum.size() - 1; i > 0; i--) {
            if (strNum[i - 1] > strNum[i] ) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for (int i = flag; i < strNum.size(); i++) {
            strNum[i] = '9';
        }
        return stoi(strNum);
    }
};
  • Time complexity: O(n) n is the number length
  • Space complexity: O(n) requires a string, and it is more convenient to convert to a string operation.
    To summarize
    this question, just think of a clear example, such as 98. Once strNum[i-1]> strNum[i] occurs (not monotonically increasing) ), first I want to subtract one from strNum[i-1] and assign 9 to strNum[i], so that the integer is 89. You can naturally think of the corresponding greedy solution.

Thinking of greed, we must also consider the order of traversal. Only by traversing from back to front can the results of the last comparison be reused.

When the final code is implemented, some skills are also needed, such as using a flag to mark where to start the assignment of 9.

Just learn the algorithm step by step, and look for "Code Random Record"!

Guess you like

Origin blog.51cto.com/15069438/2576225