-
Notifications
You must be signed in to change notification settings - Fork 25
/
NailingPlanks.cpp
152 lines (130 loc) · 4.2 KB
/
NailingPlanks.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// https://app.codility.com/programmers/lessons/14-binary_search_algorithm/nailing_planks/
//
// Task Score: 100%
// Correctness: 100%
// Performance: 100%
// Detected time complexity: O((N+M)*log(M))
//
#include <iostream>
#include <numeric>
#include <vector>
#include "NailingPlanks.h"
#include "macros.h"
using namespace std;
namespace nailingplanks
{
struct PlankStruct {
int start;
int stop;
};
#define PRINT_PLANKS(X) { \
cout << #X << " = [ "; \
for (const auto &x: X) { cout << "{" << x.start << ", " << x.stop << "} "; } \
cout << "];" << endl; \
}
//
// Task Score: 87%
// Correctness: 100%
// Performance: 75%
// Detected time complexity: O((N + M) * log(M))
//
// Put planks into a list, then iterate through the list for each
// C and pop it off if the list is empty. Return -1 if the list isn't empty.
//
// Assumptions:
// - N and M are integers within the range [1..30,000];
// - each element of arrays A, B, C is an integer within the range [1..2*M];
// - A[K] ≤ B[K].
//
// Edge conditions:
// - N never empty
//
int bruteForce(vector<int> &A, vector<int> &B, vector<int> &C)
{
const int N = A.size();
const int M = C.size();
vector<PlankStruct> remaining(N);
vector<bool> nails(2*M+1, false);
// create plank struct
for (int i=0; i<N; i++) {
remaining[i].start = A[i];
remaining[i].stop = B[i];
}
int minimum = -1;
// go through each nail, done once list is empty
vector<PlankStruct> planks;
for (int j=0; j<M; j++ ) {
if ( nails[C[j]] ) {
continue;
}
nails[C[j]] = true;
planks = remaining;
remaining.clear();
for (int i=0; i<(int)planks.size(); i++) {
if ( (C[j] < planks[i].start) || (C[j] > planks[i].stop) ) {
// Nail doesn't touch this plank
remaining.push_back(planks[i]);
} else {
// Nail is in plank, remove it from consideration
// by not adding it to remaining
}
}
// Nothing remains! This was the final nail.
if ( remaining.size() == 0 ) {
minimum = j+1;
break;
}
}
return minimum;
}
//
// Assumptions:
// - N and M are integers within the range [1..30,000];
// - each element of arrays A, B, C is an integer within the range [1..2*M];
// - A[K] ≤ B[K].
//
// Going to use a prefix sum as the basis of the binary search. Increment the
// count each time a nail is encountered
bool isNailed(int nail, const vector<int> &A, const vector<int> &B, const vector<int> &C)
{
const int N = A.size();
const int M = C.size();
// This also accounts for duplicate nails, essentially ignores them
vector<int> nails(2*M+1, 0);
for (int i = 0; i < nail; i++)
{
nails[C[i]] = 1;
}
// Create the partial sums based on the nails present
std::partial_sum(nails.begin(), nails.end(), nails.begin());
// A given plank is nailed when the number of nails changes
// within its range. Have to check the nail right before
// start though (since the nail could be put in at the first
// element. Stop as soon as we find a plank that is not nailed
bool nailed = true;
for (int i = 0; (i < N) && nailed; i++)
{
nailed = (nails[B[i]] - nails[A[i]-1]) > 0;
}
return nailed;
}
// The standard binary search mechanism
int solution(vector<int> &A, vector<int> &B, vector<int> &C)
{
int minNails = 1;
int maxNails = C.size();
int result = -1;
while (minNails <= maxNails)
{
int mid = (minNails + maxNails) / 2;
if (isNailed(mid, A, B, C))
{
maxNails = mid - 1;
result = mid;
} else {
minNails = mid + 1;
}
}
return result;
}
}