|
4 | 4 |
|
5 | 5 | #ifndef MAX_PROFIT_IN_JOB_SCHEDULING_H |
6 | 6 | #define MAX_PROFIT_IN_JOB_SCHEDULING_H |
7 | | -// We have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i]. |
8 | | -// You're given the startTime, endTime and profit arrays, return the maximum profit you can take such that there are no |
9 | | -// two jobs in the subset with overlapping time range. |
10 | | -// If you choose a job that ends at time X you will be able to start another job that starts at time X. |
11 | | -#include <vector> |
| 7 | +// We have n jobs, where every job is scheduled to be done from startTime[i] to |
| 8 | +// endTime[i], obtaining a profit of profit[i]. You're given the startTime, |
| 9 | +// endTime and profit arrays, return the maximum profit you can take such that |
| 10 | +// there are no two jobs in the subset with overlapping time range. If you |
| 11 | +// choose a job that ends at time X you will be able to start another job that |
| 12 | +// starts at time X. |
12 | 13 | #include <algorithm> |
| 14 | +#include <vector> |
13 | 15 |
|
14 | | -struct Job |
15 | | -{ |
16 | | - int start; |
17 | | - int end; |
18 | | - int profit; |
| 16 | +struct Job { |
| 17 | + int start; |
| 18 | + int end; |
| 19 | + int profit; |
19 | 20 | }; |
20 | 21 |
|
21 | | -int find_next_job(const std::vector<Job>& jobs, int current_end) |
22 | | -{ |
23 | | - int low = 0; |
24 | | - int high = jobs.size(); |
25 | | - while (low < high) |
26 | | - { |
27 | | - const int mid = (low + high) / 2; |
28 | | - if (jobs[mid].start >= current_end) |
29 | | - high = mid; |
30 | | - else |
31 | | - low = mid + 1; |
32 | | - } |
33 | | - return low; |
| 22 | +int find_next_job(const std::vector<Job> &jobs, int current_end) { |
| 23 | + int low = 0; |
| 24 | + int high = jobs.size(); |
| 25 | + while (low < high) { |
| 26 | + const int mid = (low + high) / 2; |
| 27 | + if (jobs[mid].start >= current_end) |
| 28 | + high = mid; |
| 29 | + else |
| 30 | + low = mid + 1; |
| 31 | + } |
| 32 | + return low; |
34 | 33 | } |
35 | 34 |
|
36 | 35 | // Binary search to find the last job that doesn't overlap |
37 | | -int find_last_non_conflicting(const std::vector<Job>& jobs, int job_index) |
38 | | -{ |
39 | | - int low = 0; |
40 | | - int high = job_index - 1; |
41 | | - while (low <= high) |
42 | | - { |
43 | | - const int mid = (low + high) / 2; |
44 | | - if (jobs[mid].end <= jobs[job_index].start) |
45 | | - { |
46 | | - if (jobs[mid + 1].end <= jobs[job_index].start) |
47 | | - low = mid + 1; |
48 | | - else |
49 | | - return mid; |
50 | | - } |
51 | | - else |
52 | | - high = mid - 1; |
53 | | - } |
54 | | - return -1; |
| 36 | +int find_last_non_conflicting(const std::vector<Job> &jobs, int job_index) { |
| 37 | + int low = 0; |
| 38 | + int high = job_index - 1; |
| 39 | + while (low <= high) { |
| 40 | + const int mid = (low + high) / 2; |
| 41 | + if (jobs[mid].end <= jobs[job_index].start) { |
| 42 | + if (jobs[mid + 1].end <= jobs[job_index].start) |
| 43 | + low = mid + 1; |
| 44 | + else |
| 45 | + return mid; |
| 46 | + } else |
| 47 | + high = mid - 1; |
| 48 | + } |
| 49 | + return -1; |
55 | 50 | } |
56 | 51 |
|
57 | | -int dp(int job_index, const std::vector<Job>& jobs, std::vector<int>& memo) |
58 | | -{ |
59 | | - if (job_index >= jobs.size()) |
60 | | - return 0; |
61 | | - if (memo[job_index] != -1) |
62 | | - return memo[job_index]; |
63 | | - int next = find_next_job(jobs, jobs[job_index].end); |
64 | | - int take_next_job = jobs[job_index].profit + dp(next, jobs, memo); |
65 | | - int skip_next_job = dp(job_index + 1, jobs, memo); |
66 | | - |
67 | | - return memo[job_index] = std::max(take_next_job, skip_next_job); |
| 52 | +int dp(int job_index, const std::vector<Job> &jobs, std::vector<int> &memo) { |
| 53 | + if (job_index >= jobs.size()) |
| 54 | + return 0; |
| 55 | + if (memo[job_index] != -1) |
| 56 | + return memo[job_index]; |
| 57 | + int next = find_next_job(jobs, jobs[job_index].end); |
| 58 | + int take_next_job = jobs[job_index].profit + dp(next, jobs, memo); |
| 59 | + int skip_next_job = dp(job_index + 1, jobs, memo); |
| 60 | + |
| 61 | + return memo[job_index] = std::max(take_next_job, skip_next_job); |
68 | 62 | } |
69 | 63 |
|
70 | 64 | // top-down approach |
71 | | -int job_scheduling(std::vector<int>& start_time, std::vector<int>& end_time, std::vector<int>& profit) |
72 | | -{ |
73 | | - int n = start_time.size(); |
74 | | - if (n == 0) |
75 | | - return 0; |
76 | | - std::vector<Job> jobs(n); |
77 | | - for (int i = 0; i < n; i++) |
78 | | - { |
79 | | - jobs[i] = {start_time[i], end_time[i], profit[i]}; |
80 | | - } |
81 | | - std::sort(jobs.begin(), jobs.end(), [](const Job& j1, const Job& j2) |
82 | | - { |
83 | | - return j1.start < j2.start; |
84 | | - }); |
85 | | - std::vector<int> memo(n, -1); |
86 | | - return dp(0, jobs, memo); |
| 65 | +int job_scheduling(std::vector<int> &start_time, std::vector<int> &end_time, |
| 66 | + std::vector<int> &profit) { |
| 67 | + int n = start_time.size(); |
| 68 | + if (n == 0) |
| 69 | + return 0; |
| 70 | + std::vector<Job> jobs(n); |
| 71 | + for (int i = 0; i < n; i++) { |
| 72 | + jobs[i] = {start_time[i], end_time[i], profit[i]}; |
| 73 | + } |
| 74 | + std::sort(jobs.begin(), jobs.end(), |
| 75 | + [](const Job &j1, const Job &j2) { return j1.start < j2.start; }); |
| 76 | + std::vector<int> memo(n, -1); |
| 77 | + return dp(0, jobs, memo); |
87 | 78 | } |
88 | 79 |
|
89 | 80 | // bottom-up approach |
90 | | -int job_scheduling_bottom_up(std::vector<int>& start_time, std::vector<int>& end_time, std::vector<int>& profit) |
91 | | -{ |
92 | | - int n = start_time.size(); |
93 | | - if (n == 0) |
94 | | - return 0; |
95 | | - std::vector<Job> jobs(n); |
96 | | - for (int i = 0; i < n; i++) |
97 | | - { |
98 | | - jobs[i] = {start_time[i], end_time[i], profit[i]}; |
99 | | - } |
100 | | - std::sort(jobs.begin(), jobs.end(), [](const Job& j1, const Job& j2) |
101 | | - { |
102 | | - return j1.end < j2.end; |
103 | | - }); |
104 | | - std::vector<int> dp(n); |
105 | | - dp[0] = jobs[0].profit; |
106 | | - for (int job_index = 1; job_index < n; ++job_index) |
107 | | - { |
108 | | - int profit_if_taken = jobs[job_index].profit; |
109 | | - |
110 | | - int last_compatible_index = find_last_non_conflicting(jobs, job_index); |
111 | | - if (last_compatible_index != -1) |
112 | | - { |
113 | | - profit_if_taken += dp[last_compatible_index]; |
114 | | - } |
115 | | - |
116 | | - dp[job_index] = std::max(dp[job_index - 1], profit_if_taken); |
| 81 | +int job_scheduling_bottom_up(std::vector<int> &start_time, |
| 82 | + std::vector<int> &end_time, |
| 83 | + std::vector<int> &profit) { |
| 84 | + int n = start_time.size(); |
| 85 | + if (n == 0) |
| 86 | + return 0; |
| 87 | + std::vector<Job> jobs(n); |
| 88 | + for (int i = 0; i < n; i++) { |
| 89 | + jobs[i] = {start_time[i], end_time[i], profit[i]}; |
| 90 | + } |
| 91 | + std::sort(jobs.begin(), jobs.end(), |
| 92 | + [](const Job &j1, const Job &j2) { return j1.end < j2.end; }); |
| 93 | + std::vector<int> dp(n); |
| 94 | + dp[0] = jobs[0].profit; |
| 95 | + for (int job_index = 1; job_index < n; ++job_index) { |
| 96 | + int profit_if_taken = jobs[job_index].profit; |
| 97 | + |
| 98 | + int last_compatible_index = find_last_non_conflicting(jobs, job_index); |
| 99 | + if (last_compatible_index != -1) { |
| 100 | + profit_if_taken += dp[last_compatible_index]; |
117 | 101 | } |
118 | 102 |
|
119 | | - return dp[n - 1]; |
120 | | -} |
121 | | - |
| 103 | + dp[job_index] = std::max(dp[job_index - 1], profit_if_taken); |
| 104 | + } |
122 | 105 |
|
123 | | -int compact_top_down(const std::vector<int> & start, const std::vector<int> & end, std::vector<int> & profit) |
124 | | -{ |
125 | | - struct Job |
126 | | - { |
127 | | - int s; |
128 | | - int e; |
129 | | - int p; |
130 | | - }; |
131 | | - int n = start.size(); |
132 | | - std::vector<Job> jobs(n); |
133 | | - for (int i{}; i < n; ++i) |
134 | | - { |
135 | | - jobs[i] = {start[i], end[i], profit[i]}; |
136 | | - } |
137 | | - std::sort(jobs.begin(), jobs.end(), [](const Job& j1, const Job& j2){ return j1.s < j2.s; }); |
138 | | - std::vector<int> sorted_starts(n); |
139 | | - for (int i{}; i < n; ++i) |
140 | | - { |
141 | | - sorted_starts[i] = jobs[i].s; |
142 | | - } |
143 | | - std::vector<int> memo(n ,-1); |
144 | | - |
145 | | - std::function<int(int)> dfs = [&](int i)-> int |
146 | | - { |
147 | | - if (i>n-1) |
148 | | - return 0; |
149 | | - if (memo[i] != -1) |
150 | | - return memo[i]; |
151 | | - int best = dfs(i+1); |
152 | | - int j = int(std::lower_bound(sorted_starts.begin(), sorted_starts.end(), jobs[i].e) - sorted_starts.begin()); |
153 | | - best = std::max(best, jobs[i].p + dfs(j)); |
154 | | - return memo[i] = best; |
155 | | - }; |
156 | | - return dfs(0); |
| 106 | + return dp[n - 1]; |
157 | 107 | } |
158 | 108 |
|
| 109 | +int compact_top_down(const std::vector<int> &start, const std::vector<int> &end, |
| 110 | + std::vector<int> &profit) { |
| 111 | + struct Job { |
| 112 | + int start; |
| 113 | + int end; |
| 114 | + int profit; |
| 115 | + }; |
| 116 | + int n = start.size(); |
| 117 | + std::vector<Job> jobs(n); |
| 118 | + for (int i{}; i < n; ++i) { |
| 119 | + jobs[i] = {start[i], end[i], profit[i]}; |
| 120 | + } |
| 121 | + std::sort(jobs.begin(), jobs.end(), |
| 122 | + [](const Job &j1, const Job &j2) { return j1.start < j2.start; }); |
| 123 | + std::vector<int> sorted_starts(n); |
| 124 | + for (int i{}; i < n; ++i) { |
| 125 | + sorted_starts[i] = jobs[i].start; |
| 126 | + } |
| 127 | + std::vector<int> memo(n, -1); |
| 128 | + |
| 129 | + std::function<int(int)> dfs = [&](int current_job_index) -> int { |
| 130 | + if (current_job_index > n - 1) |
| 131 | + return 0; |
| 132 | + if (memo[current_job_index] != -1) |
| 133 | + return memo[current_job_index]; |
| 134 | + // skip i, walk down all the way to n, dfs-like |
| 135 | + int best = dfs(current_job_index + 1); |
| 136 | + // take i: jump to first job with start >= jobs[i].e, i is the current |
| 137 | + int next_job_index = |
| 138 | + int(std::lower_bound(sorted_starts.begin(), sorted_starts.end(), |
| 139 | + jobs[current_job_index].end) - |
| 140 | + sorted_starts.begin()); |
| 141 | + best = std::max(best, jobs[current_job_index].profit + dfs(next_job_index)); |
| 142 | + return memo[current_job_index] = best; |
| 143 | + }; |
| 144 | + return dfs(0); |
| 145 | +} |
159 | 146 |
|
160 | | -int compact_bottom_up(const std::vector<int> & start, const std::vector<int> & end, std::vector<int> & profit) |
161 | | -{ |
162 | | - struct Job |
163 | | - { |
164 | | - int s; |
165 | | - int e; |
166 | | - int p; |
167 | | - }; |
168 | | - int n = start.size(); |
169 | | - std::vector<Job> jobs(n); |
170 | | - for (int i{}; i < n; ++i) |
171 | | - { |
172 | | - jobs[i] = {start[i], end[i], profit[i]}; |
173 | | - } |
174 | | - std::sort(jobs.begin(), jobs.end(), [](const Job& j1, const Job& j2){ return j1.e < j2.e; }); |
175 | | - |
176 | | - std::vector<int> sorted_ends(n); |
177 | | - for (int i{}; i < n; ++i) |
178 | | - { |
179 | | - sorted_ends[i] = jobs[i].e; |
180 | | - } |
181 | | - auto pred = [&](int i)-> int |
182 | | - { |
183 | | - return int(std::upper_bound(sorted_ends.begin(), sorted_ends.end(), jobs[i].s) - sorted_ends.begin()) - 1; |
184 | | - }; |
185 | | - std::vector<int> dp(n+1, 0); |
186 | | - for (int i=1; i <= n; ++i) |
187 | | - { |
188 | | - int j =pred(i-1); |
189 | | - int take = jobs[i-1].p + dp[j+1]; |
190 | | - dp[i] = std::max(dp[i-1], take); |
191 | | - } |
192 | | - return dp[n]; |
| 147 | +int compact_bottom_up(const std::vector<int> &start, |
| 148 | + const std::vector<int> &end, std::vector<int> &profit) { |
| 149 | + struct Job { |
| 150 | + int start; |
| 151 | + int end; |
| 152 | + int profit; |
| 153 | + }; |
| 154 | + int n = start.size(); |
| 155 | + std::vector<Job> jobs(n); |
| 156 | + for (int i{}; i < n; ++i) { |
| 157 | + jobs[i] = {start[i], end[i], profit[i]}; |
| 158 | + } |
| 159 | + std::sort(jobs.begin(), jobs.end(), |
| 160 | + [](const Job &j1, const Job &j2) { return j1.end < j2.end; }); |
| 161 | + |
| 162 | + std::vector<int> sorted_ends(n); |
| 163 | + for (int i{}; i < n; ++i) { |
| 164 | + sorted_ends[i] = jobs[i].end; |
| 165 | + } |
| 166 | + auto pred = [&](int i) -> int { |
| 167 | + return int(std::upper_bound(sorted_ends.begin(), sorted_ends.end(), |
| 168 | + jobs[i].start) - |
| 169 | + sorted_ends.begin()) - |
| 170 | + 1; |
| 171 | + }; |
| 172 | + std::vector<int> dp(n + 1, 0); |
| 173 | + for (int i = 1; i <= n; ++i) { |
| 174 | + int j = pred(i - 1); |
| 175 | + int take = jobs[i - 1].profit + dp[j + 1]; |
| 176 | + dp[i] = std::max(dp[i - 1], take); |
| 177 | + } |
| 178 | + return dp[n]; |
193 | 179 | } |
194 | 180 |
|
195 | | -#endif //MAX_PROFIT_IN_JOB_SCHEDULING_H |
| 181 | +#endif // MAX_PROFIT_IN_JOB_SCHEDULING_H |
0 commit comments