LeetCode buckle biweekly 63

Keywords: Algorithm leetcode greedy algorithm

The happiest thing at the weekend is to fill in questions and write solutions to cook for his wife.

Weekly portal

2037. Make each student have the minimum number of seat movements

Idea: greed

Time complexity: O ( n ∗ lg ⁡ n ) O(n*\lg n) O(n∗lgn)

A greedy strategy is to always let the leftmost student choose the leftmost seat among the unmatched students and seats.

Try to prove it. Imagine the student on the far left A A A chose a seat on the right r r r. There must be another student B B B choose the leftmost seat l l l.

The positions of the four are set as P A P_A PA​, P r P_r Pr​, P B P_B PB​, P l P_l Pl​. Then the number of moves of the above matching scheme is ∣ P A − P r ∣ + ∣ P B − P l ∣ |P_A-P_r| + |P_B-P_l| ∣PA​−Pr​∣+∣PB​−Pl​∣

Readily available by definition:

  • P A ≤ P B P_A \le P_B PA​≤PB​
  • P l ≤ P r P_l \le P_r Pl​≤Pr​

So exchange A A A and B B Number of movements behind the seat of B ∣ P A − P l ∣ + ∣ P B − P r ∣ |P_A-P_l| + |P_B-P_r| ∣ PA − Pl ∣ + ∣ PB − Pr ∣ will certainly not exceed the former. Therefore, there must be a matching scheme that "the leftmost student chooses the leftmost seat".

When we remove the leftmost students and seats, the rest n − 1 n-1 n − 1 student and seat form a new sub problem, and the above greedy strategy is also applicable~

class Solution {
public:
    int minMovesToSeat(vector<int>& seats, vector<int>& students) {
        sort(seats.begin(), seats.end());
        sort(students.begin(), students.end());

        int anw = 0;
        for (int i = 0; i < seats.size(); i++) {
            anw += abs(seats[i] - students[i]);
        }

        return anw;
    }
};

2038. If two adjacent colors are the same, delete the current color

Idea: compare the number of characters that can be deleted

Time complexity: O ( n ) O(n) O(n)

According to the rules of the game, only one character can be deleted after each operation, and there will be no other changes. Therefore, directly count the quantity of 'A' and 'B' that can be deleted. Alice can win if and only if the current one is greater than the latter.

In the following implementations, each location needs to be compared at most three times. Do bosses have a more concise implementation?

class Solution {
public:
    bool winnerOfGame(string colors) {
        int A = 0, B = 0;
        for (int i = 2; i < colors.size(); i++) {
            // Judge whether three consecutive characters are the same. If they are the same, it means that the middle character can be deleted.
            if (colors[i] == colors[i-1] && colors[i] == colors[i-2]) {
                (colors[i] == 'A') ? ++A : ++B;
            }
        }
        return A > B;
    }
};

2039. When the network is idle

Idea: find the shortest path by BFS

Time complexity: O ( n + e ) O(n+e) O(n+e)

First, use BFS to calculate the node 0 0 The shortest path from 0 to all other nodes. node i i The round trip time of i is recorded as T i T_i Ti​. Combined with the meaning of the question, it is easy to get nodes i i i the last time the message was sent is:
l a s t i = T i − 1 p a t i e n c e i ∗ p a t i e n c e i last_i = \frac{T_i - 1}{patience_i}*patience_i lasti​=patiencei​Ti​−1​∗patiencei​

So the final answer is:
max ⁡ i = 1 n ( l a s t i + T i ) \max_{i=1}^{n}(last_i + T_i) i=1maxn​(lasti​+Ti​)

class Solution {
public:
    vector<int> edge[100001];
    int len[100001];
    void bfs() {
        queue<int> q;
        q.push(0);
        memset(len, -1, sizeof(len));
        len[0] = 0;
        while(!q.empty()) {
            auto f = q.front();
            q.pop();
            for (auto next : edge[f]) {
                if (len[next] == -1) {
                    len[next] = len[f]+1;
                    q.push(next);
                }
            }
        }
    }
    int networkBecomesIdle(vector<vector<int>>& edges, vector<int>& patience) {
        // Building edge tables
        for (const auto &e : edges) {
            edge[e[0]].emplace_back(e[1]);
            edge[e[1]].emplace_back(e[0]);   
        }
        // Solve the shortest path from node 0 to other nodes
        bfs();
        
        // Find the largest $T[i] + last[i]$
        int anw = 0;
        for (int i = 1; i < patience.size(); i++) {
            int T = len[i]*2;
            T += (T-1)/patience[i]*patience[i];
            anw = max(anw, T);
        }
        return anw+1;
    }
};

2040. The K-th minor product of two ordered arrays

Idea: dichotomy

Time complexity: O ( n ∗ lg ⁡ ( m + n ) ) O(n*\lg (m+n)) O(n∗lg(m+n)). n n n is the length of the array, m m m is the value range of the product.

Set integer x x x. If less than x x The product of x is less than k k k. Then the answer is not less than x x x. On the contrary, the answer is less than x x x.

Obviously, you can get through two points x x The value of x is O ( lg ⁡ m ) O(\lg m) Find the answer under the time complexity of O(lgm).

Finally, how to determine less than x x What about the number of products of x? You can first n u m s 2 nums2 nums2 sort and enumerate n u m s 1 nums1 The elements in nums1 are not difficult to find for certain n u m s 1 i nums1_i nums1i​ , n u m s 1 i ∗ n u m s j nums1_i*nums_j nums1i * numsj is about j j j monotonically varying. Therefore, it can be met with the help of binary fast positioning n u m s 1 i ∗ n u m s j < x nums1_i*nums_j\lt x nums1i * numsj < x j j Scope of j. Therefore, the time complexity of the process is O ( n ∗ lg ⁡ n ) O(n*\lg n) O(n∗lgn).

To sum up, it can be divided into two parts O ( n ∗ lg ⁡ ( m + n ) ) O(n*\lg (m+n)) The answer is obtained under the time complexity of O(n * lg(m+n)).

class Solution {
  public:
    int64_t cal(std::vector<int> &pos, std::vector<int> &neg, std::vector<int> &B, int64_t goal) {
      // When positive, the product is monotonically increasing with respect to j
      int64_t cnt = 0;
      for (int i = 0 ; i < pos.size(); i++) {
        int l = 0, r = B.size()-1;
        while (l <= r) {
          int mid = (l+r)>>1;
          if (pos[i]*1L*B[mid] >= goal) {
            r = mid-1;
          } else {
            l = mid+1;
          }
        }
        cnt += l;
      }

      // When negative, the product is monotonically decreasing with respect to j
      for (int i = 0, j = 0; i < neg.size(); i++) {
        int l = 0, r = B.size()-1;
        while (l <= r) {
          int mid = (l+r)>>1;
          if (neg[i]*1L*B[mid] >= goal) {
            l = mid+1;
          } else {
            r = mid-1;
          }
        }
        cnt += (B.size()-r-1);
      }

      return cnt;
    }

    long long kthSmallestProduct(vector<int>& A, vector<int>& B, long long k) {
      // First distinguish the positive and negative numbers for easy processing
      vector<int> pos, neg;
      for (auto a : A) {
        if (a < 0) { neg.emplace_back(a); }
        else if (a > 0) { pos.emplace_back(a); }
      }

      // l and r are the boundary of the first layer dichotomy
      int64_t l = -1 * 1e11;
      int64_t r = 1e11;
      while (l <= r) {
        int64_t x = (l+r)>>1;
        // Count the number of products less than x
        int64_t cnt = cal(pos, neg, B, x);

        // Note that A[i] == 0 is not counted in cal, and it is added here.
        if (0 < x) {
          cnt += (A.size() - pos.size() - neg.size())*B.size();
        }
        // CNT < K, the answer may be in [x+1, r] or X.
        if (cnt < k) {
          l = x+1;
        } else {
          // The answer must be in [l, x-1].
          r = x-1;
        }
      }
      // It is not difficult to find that the last time l is moved, x is the answer, so l-1 is the answer.
      return l-1;
    }
};

Posted by jxrd on Fri, 22 Oct 2021 19:50:27 -0700