Exercises on Codility (12) and exercises on codility 12
(1) MinMaxDivision
Given a non-negative integer array, each integer is [0 .. m], you want to divide it into K segments (cut K-1 knife), the segment can be empty, each element must belong to a segment, each segment must contain 0 or more continuous elements. The sum of the best and maximum segments is required to be as small as possible, and the smallest and largest sum is returned.
Data range: N, K [1 .. 10 ^ 5], M [0 .. 10 ^ 4]
The complexity time O (N * log (N + M) Space O (1) is required ).
Analysis: typical binary classification is acceptable. Divide the sum of two largest segments, and then we add them one by one. If we exceed the value to be added, we start a new one. This method is more effective than non-negative integers ......
To put it simply, the architecture of the binary problem is the binary + judgment. The judgment part is obviously O (N. The complexity of binary is determined by the interval of binary. The left endpoint of our binary interval can be considered as min (A [I]) or 0, and the larger point of the anyway interval is also related. The maximum right endpoint is N * M, the complexity of the Second Division is O (log (N * M) = O (logN + logM) = O (2 * log (max (M, N )) = O (log (max (M, N) = O (log (M + N) So the complexity of the algorithm detection has reached the requirement.
// you can use includes, for example:// #include <algorithm>// you can write to stdout for debugging purposes, e.g.// cout << "this is a debug message" << endl;bool can(vector<int> &a,int x, int k) {int sum = 0; --k; for (int i = 0; i < a.size();) { if ((sum += a[i]) > x) { if (--k < 0) { return false; } sum = 0; } else { ++i; } } return true;}int solution(int K, int M, vector<int> &A) { // write your code in C++98 int left = 0,right = -1; for (int i = 0; i < A.size(); ++i) { right += A[i]; left = max(left, A[i]); } while (left <= right) { int mid = (left + right) >> 1; if (can(A, mid, K)) { right = mid - 1; } else { left = mid + 1; } } return right + 1; }
(2) NailingPlanks
N boards can be considered as N line segments. Two positive integer arrays A [], B [], [A [k] with N length are given. B [k] indicates the start and end points of A plank (Line Segment). A [k] <= B [k]. There are M nails in an array of positive integers whose lengths are M. Nail I can hold the wooden board K, and now it is A [K] <= C [I] <= B [K]. How many nails can be used to hold all boards in order? -1 is returned.
Data range: the range of the number of boards N and M [1 .. 30000], and the range of elements in array a B C is [1 .. 2 * M]
Complexity required: time O (N + M) * log (M), Space O (M)
Analysis: an obvious and compliant algorithm is the second answer. The question is how to judge. Obviously, we cannot cycle boards and nails. However, we can calculate the total number of nails from the beginning to the current position. This is the idea of prefix and. The prefix and O (M) are required for calculation, and O (N) is required for determination. The second part is O (logM). Therefore, time complexity is required for better processing. The prefix and O (M) must be stored in the space ).
Code:
// you can also use includes, for example:// #include <algorithm>int solution(vector<int> &A, vector<int> &B, vector<int> &C) { // write your code in C++98 int m = C.size(); int M = (m << 1) | 1; int left = 0, right = m, result = -1; while (left <= right) { int mid = (left + right) >> 1; vector<int> v; v.resize(M, 0); for (int i = 0; i < mid; ++i) { ++v[C[i]]; } for (int i = 1; i < M; ++i) { v[i] += v[i - 1]; } bool can = true; for (int i = 0; i < A.size(); ++i) { if (v[B[i]] - v[A[i] - 1] == 0) { can = false; break; } } if (can) { result = mid; right = mid - 1; } else { left = mid + 1; } } return result;}
Faster algorithms. If we create an array of 2 * M in length, each position indicates the minimum number of nails in the position (maybe there are multiple nails in the same position, take the smallest number). The value of the position without a nail is infinite. The minimum number of Nails Fixed on block I is equivalent to the minimum value in the range [A [I], B [I. But our question is actually to find the max of these minimum values. First, if the coverage range of one board A completely includes another board B, we can only consider board B. Because fixed board B can fix board A at the same time, and we must fix board B, even if A covers A range with A smaller value, it cannot change the final maximum value.
Therefore, we can create an array plank [x] to indicate the maximum left boundary of the Board with the right endpoint x. If there is no wood board, the boundary is 0. We traverse the right border of the Board from left to right. Suppose that the previous (left) Board has been fixed, and the fixed range is [left, right) (right open range ), then, if it is clear that the right boundary of the current Board is greater (we traverse the right boundary in the ascending order), if the start of the board is <= left, then it is fixed by the front and does not affect the result. Otherwise, the maximum value between [start, end] is required. This problem is a bit like the maximum value of the sliding window. The essence is: we constantly query the maximum value. When each query is performed, the left and right boundary of the window increase monotonically, so we can dynamically update the window to maintain the maximum value. This classic problem can be implemented using monotonous queues, which brings monotonous queues to the extreme.
Conclusion: When querying the maximum value of a window, if the left and right boundary of the window are single-incrementing during the sliding to the right, monotonous queues can be used.
The time complexity O (N + M) is linear.
Code:
// you can also use includes, for example:// #include <algorithm>#include <deque>const int inf = 2000000000;int solution(vector<int> &A, vector<int> &B, vector<int> &C) { // write your code in C++98 int m = C.size(), M = (m << 1) | 1; vector<int> nail(M, inf); for (int i = m - 1; i >= 0; --i) { nail[C[i]] = i; } vector<int> plank(M, 0); for (int i = 0; i < A.size(); ++i) { plank[B[i]] = max(plank[B[i]], A[i]); } int left = 0, right = 0, r = 0; deque<int> q; for (int i = 1; i < M; ++i) { if (plank[i] > left) { left = plank[i]; while ((!q.empty()) && (q.front() < left)) { q.pop_front(); } for (right = max(right, left); right <= i; ++right) { while ((!q.empty()) && (nail[q.back()] >= nail[right])) { q.pop_back(); } q.push_back(right); } r = max(r, nail[q.front()]); if (r >= inf) { return -1; } } } return r + 1;}