|
| 1 | +--- |
| 2 | +id: Booking-concert-tickets-in-groups |
| 3 | +title: Booking Concert Tickets in Groups |
| 4 | +sidebar_label: 2286 Booking Concert Tickets in Groups |
| 5 | +tags: |
| 6 | +- Java |
| 7 | +- Binary Search |
| 8 | +- Design |
| 9 | +- Segmnent Tree |
| 10 | +- Binary Indexed Tree |
| 11 | +description: "This document provides a solution where we need to design a ticketing system that can allocate seats." |
| 12 | +--- |
| 13 | + |
| 14 | +## Problem |
| 15 | + |
| 16 | +A concert hall has $n$ rows numbered from $0$ to $n - 1$, each with $m$ seats, numbered from $0$ to $m - 1$. You need to design a ticketing system that can allocate seats in the following cases: |
| 17 | + |
| 18 | +- If a group of $k$ spectators can sit **together** in a row. |
| 19 | + |
| 20 | +- If **every** member of a group of $k$ spectators can get a seat. They may or **may not** sit together. |
| 21 | + |
| 22 | +Note that the spectators are very picky. Hence: |
| 23 | + |
| 24 | +- They will book seats only if each member of their group can get a seat with row number **less than or equal** to $maxRow$. $maxRow$ can vary from group to group. |
| 25 | + |
| 26 | +- In case there are multiple rows to choose from, the row with the **smallest** number is chosen. If there are multiple seats to choose in the same row, the seat with the **smallest** number is chosen. |
| 27 | + |
| 28 | +Implement the $BookMyShow$ class: |
| 29 | + |
| 30 | +- $BookMyShow(int n, int m)$ Initializes the object with $n$ as number of rows and $m$ as number of seats per row. |
| 31 | + |
| 32 | +- $int[] gather(int k, int maxRow)$ Returns an array of length $2$ denoting the row and seat number (respectively) of the **first seat** being allocated to the $k$ members of the group, who must sit **together**. In other words, it returns the smallest possible $r$ and $c$ such that all $[c, c + k - 1]$ seats are valid and empty in row $r$, and $r <= maxRow$. Returns $[]$ in case it is **not possible** to allocate seats to the group. |
| 33 | + |
| 34 | +- $boolean scatter(int k, int maxRow)$ Returns $true$ if all $k$ members of the group can be allocated seats in rows $0$ to $maxRow$, who may or **may not** sit together. If the seats can be allocated, it allocates $k$ seats to the group with the **smallest** row numbers, and the smallest possible seat numbers in each row. Otherwise, returns $false$. |
| 35 | + |
| 36 | +### Examples |
| 37 | + |
| 38 | +**Example 1:** |
| 39 | + |
| 40 | +``` |
| 41 | +Input: ["BookMyShow", "gather", "gather", "scatter", "scatter"] |
| 42 | +[[2, 5], [4, 0], [2, 0], [5, 1], [5, 1]] |
| 43 | +
|
| 44 | +Output: [null, [0, 0], [], true, false] |
| 45 | +
|
| 46 | +Explanation: |
| 47 | +
|
| 48 | +BookMyShow bms = new BookMyShow(2, 5); // There are 2 rows with 5 seats each |
| 49 | +bms.gather(4, 0); // return [0, 0] |
| 50 | + // The group books seats [0, 3] of row 0. |
| 51 | +
|
| 52 | +bms.gather(2, 0); // return [] |
| 53 | + // There is only 1 seat left in row 0, |
| 54 | + // so it is not possible to book 2 consecutive seats. |
| 55 | +
|
| 56 | +bms.scatter(5, 1); // return True |
| 57 | + // The group books seat 4 of row 0 and seats [0, 3] of row 1. |
| 58 | +
|
| 59 | +bms.scatter(5, 1); // return False |
| 60 | + // There is only one seat left in the hall. |
| 61 | +``` |
| 62 | + |
| 63 | +### Constraints |
| 64 | + |
| 65 | +- $1 <= n <= 5 * 10^4$ |
| 66 | +- $1 <= m, k <= 10^9$ |
| 67 | +- $0 <= maxRow <= n - 1$ |
| 68 | +- At most $5 * 10^4$ calls in total will be made to $gather$ and $scatter$. |
| 69 | +--- |
| 70 | + |
| 71 | +## Approach |
| 72 | + |
| 73 | +To solve the problem, we need to understand the nature of the allowed moves: |
| 74 | + |
| 75 | +1. **Segment Tree**: |
| 76 | + |
| 77 | + - **Build**: Construct the segment tree with initial values, where each node stores the sum and maximum number of seats in the range it represents. |
| 78 | + |
| 79 | + - **Update**: Update the segment tree when seats are booked, modifying the relevant nodes to reflect the new seat counts. |
| 80 | + |
| 81 | + - **Queries**: |
| 82 | + |
| 83 | + - **Gather Query**: Find a row with at least **'k'** seats available within the first **'maxRow'** rows. |
| 84 | + |
| 85 | + - **Sum Query**: Compute the total number of seats available within the first **'maxRow'** rows. |
| 86 | + |
| 87 | +2. **BookMyShow Class**: |
| 88 | + |
| 89 | + - **Constructor**: Initialize the segment tree and an array to keep track of seats available in each row. |
| 90 | + |
| 91 | + - **Gather**: Check if there is a row with at least **'k'** seats and update the tree and row seat count accordingly. |
| 92 | + |
| 93 | + - **Scatter**: Allocate **'k'** |
| 94 | + |
| 95 | +## Solution for Booking Concert Tickets in Groups |
| 96 | + |
| 97 | +- The problem revolves around efficiently managing and querying seat allocations in a theater. The segment tree is used to handle range queries and updates swiftly, ensuring that operations like finding available seats or updating seat counts are performed in logarithmic time. |
| 98 | + |
| 99 | +#### Code in Java |
| 100 | + |
| 101 | +```java |
| 102 | +class BookMyShow { |
| 103 | + static class SegTree{ |
| 104 | + long sum[]; // store sum of seats in a range |
| 105 | + long segTree[]; // store maximum seats in a range |
| 106 | + int m, n; |
| 107 | + public SegTree(int n, int m) { |
| 108 | + this.m = m; |
| 109 | + this.n = n; |
| 110 | + segTree = new long[4*n]; |
| 111 | + sum = new long[4*n]; |
| 112 | + build(0, 0, n-1, m); |
| 113 | + } |
| 114 | + |
| 115 | + private void build(int index, int lo, int hi, long val){ |
| 116 | + if(lo == hi){ |
| 117 | + segTree[index] = val; // initialize segement tree with initial seat capacity |
| 118 | + sum[index] = val; // initialize "sum" with initial seat capacity of a row |
| 119 | + return; |
| 120 | + } |
| 121 | + int mid = (lo + hi)/2; |
| 122 | + build(2*index +1, lo, mid, val); // build left sub tree |
| 123 | + build(2*index +2, mid+1, hi, val); // build right sub tree |
| 124 | + segTree[index] = Math.max(segTree[2*index + 1], segTree[2*index + 2]); // maximum seats in a row for subtrees |
| 125 | + sum[index] = sum[2*index + 1] + sum[2*index + 2]; // sum of seats in a range |
| 126 | + } |
| 127 | + |
| 128 | + private void update(int index, int lo, int hi, int pos, int val){ |
| 129 | + /** |
| 130 | + Method to update segment tree based on the available seats in a row |
| 131 | + **/ |
| 132 | + if(lo == hi){ |
| 133 | + segTree[index] = val; |
| 134 | + sum[index] = val; |
| 135 | + return; |
| 136 | + } |
| 137 | + int mid = (lo + hi) / 2; |
| 138 | + if (pos <= mid) { // position to update is in left |
| 139 | + update(2 * index + 1, lo, mid, pos, val); |
| 140 | + } else { // position to update is in right |
| 141 | + update(2 * index + 2, mid+1, hi, pos, val); |
| 142 | + } |
| 143 | + // update segment tree and "sum" based on the update in "pos" index |
| 144 | + segTree[index] = Math.max(segTree[2*index + 1] , segTree[2*index + 2]); |
| 145 | + sum[index] = sum[2*index + 1] + sum[2*index + 2]; |
| 146 | + } |
| 147 | + |
| 148 | + public void update(int pos, int val){ |
| 149 | + update(0, 0, n - 1 , pos, val); |
| 150 | + } |
| 151 | + |
| 152 | + public int gatherQuery(int k, int maxRow){ |
| 153 | + return gatherQuery(0, 0, n - 1 , k, maxRow); |
| 154 | + } |
| 155 | + |
| 156 | + private int gatherQuery(int index, int lo, int hi, int k, int maxRow){ |
| 157 | + /** |
| 158 | + Method to check if seats are available in a single row |
| 159 | + **/ |
| 160 | + if(segTree[index] < k || lo > maxRow) |
| 161 | + return -1; |
| 162 | + if(lo == hi) return lo; |
| 163 | + int mid = (lo + hi) / 2; |
| 164 | + int c = gatherQuery(2*index + 1, lo, mid, k, maxRow); |
| 165 | + if(c == -1){ |
| 166 | + c = gatherQuery(2*index + 2, mid +1, hi, k, maxRow); |
| 167 | + } |
| 168 | + return c; |
| 169 | + } |
| 170 | + |
| 171 | + public long sumQuery(int k, int maxRow){ |
| 172 | + return sumQuery(0, 0, n-1, k, maxRow); |
| 173 | + } |
| 174 | + |
| 175 | + private long sumQuery(int index, int lo, int hi, int l, int r){ |
| 176 | + if(lo > r || hi < l ) return 0; // not in range |
| 177 | + if(lo >= l && hi <= r) return sum[index]; // in range |
| 178 | + int mid = (lo + hi)/2; |
| 179 | + return sumQuery(2*index+1, lo, mid, l, r) + sumQuery(2*index+2, mid+1, hi, l, r); |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + SegTree segTree; |
| 184 | + int[] rowSeats; // stores avaiable seats in a row, helps to find the vacant seat in a row |
| 185 | + |
| 186 | + public BookMyShow(int n, int m) { |
| 187 | + segTree = new SegTree(n, m); |
| 188 | + rowSeats = new int[n]; |
| 189 | + Arrays.fill(rowSeats, m); // initialize vacant seats count to "m" for all the rows |
| 190 | + } |
| 191 | + |
| 192 | + |
| 193 | + public int[] gather(int k, int maxRow) { |
| 194 | + int row = segTree.gatherQuery(k, maxRow); // find row which has k seats |
| 195 | + if(row == -1) return new int[]{}; // can't find a row with k seats |
| 196 | + int col = segTree.m - rowSeats[row]; // find column in the row which has k seats |
| 197 | + rowSeats[row] -= k; // reduce the seats |
| 198 | + segTree.update(row, rowSeats[row]); // update the segment tree |
| 199 | + return new int[]{row, col}; |
| 200 | + |
| 201 | + } |
| 202 | + |
| 203 | + public boolean scatter(int k, int maxRow) { |
| 204 | + long sum = segTree.sumQuery(0, maxRow); // find the sum for the given range [0, maxRow] |
| 205 | + if(sum < k) return false; // can't find k seats in [0, maxRow] |
| 206 | + |
| 207 | + for(int i=0; i<=maxRow && k !=0 ; i++){ |
| 208 | + if(rowSeats[i] > 0){ // if current row has seats then allocate those seats |
| 209 | + long t = Math.min(rowSeats[i], k); |
| 210 | + rowSeats[i] -= t; |
| 211 | + k -= t; |
| 212 | + segTree.update(i,rowSeats[i]); // update the segment tree |
| 213 | + } |
| 214 | + } |
| 215 | + return true; |
| 216 | + } |
| 217 | +} |
| 218 | +``` |
| 219 | + |
| 220 | +### Complexity Analysis |
| 221 | + |
| 222 | +#### Time Complexity: $O(logn)$ |
| 223 | + |
| 224 | +> **Reason**: Segment tree operations are build: $O(n)$, update: $O(log n)$, gather query: $O(log n)$, sum query: $O(log n)$; BookMyShow operations are constructor: $O(n)$, gather: $O(log n)$, scatter: $O(n log n)$. |
| 225 | +
|
| 226 | +#### Space Complexity: $O(n)$ |
| 227 | + |
| 228 | +> **Reason**: The space complexity is $O(n)$, The **'rowSeats'** array requires O(n) space to store the seat counts for each row. |
| 229 | +
|
| 230 | +# References |
| 231 | + |
| 232 | +- **LeetCode Problem:** [Booking Concert Tickets in Groups](https://leetcode.com/problems/booking-concert-tickets-in-groups/description/) |
| 233 | +- **Solution Link:** [Booking Concert Tickets in Groups Solution on LeetCode](https://leetcode.com/problems/booking-concert-tickets-in-groups/solutions/) |
| 234 | +- **Authors LeetCode Profile:** [Vivek Vardhan](https://leetcode.com/u/vivekvardhan43862/) |
0 commit comments