class AppointmentBook {
    private boolean[][] minutesFree;

    public AppointmentBook(int periods, int minutes) {
        minutesFree = new boolean[periods + 1][minutes];
        for (boolean[] period : minutesFree)
            java.util.Arrays.fill(period, true);
    }

    private boolean isMinuteFree(int period, int minute) {
        return minutesFree[period][minute];
    }

    public int findFreeBlock(int period, int duration) {
        int maxStart = 60 - duration;

        for (int startMinute = 0; startMinute <= maxStart; startMinute++) {
            boolean blockFree = true;

            for (int offset = 0; offset < duration; offset++) {
                if (!isMinuteFree(period, startMinute + offset)) {
                    blockFree = false;
                    break;
                }
            }

            if (blockFree) {
                return startMinute;
            }
        }

        return -1;
    }
}

public class MyProgram {
    public static void main(String[] args) {
        AppointmentBook book = new AppointmentBook(6, 60);
        System.out.println(book.findFreeBlock(1, 15));
        System.out.println(book.findFreeBlock(3, 60));
        System.out.println(book.findFreeBlock(5, 30));
    }
}
MyProgram.main(null);

Part A


Part B: makeAppointment

Search across a range of periods for the earliest available block of the given duration, reserve it if found, and return whether a booking was made.

class AppointmentBook {
    private boolean[][] minutesFree;

    public AppointmentBook(int periods, int minutes) {
        minutesFree = new boolean[periods + 1][minutes];
        for (boolean[] period : minutesFree)
            java.util.Arrays.fill(period, true);
    }

    private boolean isMinuteFree(int period, int minute) {
        return minutesFree[period][minute];
    }

    private void reserveBlock(int period, int startMinute, int duration) {
        for (int i = 0; i < duration; i++) {
            minutesFree[period][startMinute + i] = false;
        }
    }

    public int findFreeBlock(int period, int duration) {
        int maxStart = 60 - duration;

        for (int startMinute = 0; startMinute <= maxStart; startMinute++) {
            boolean blockFree = true;

            for (int offset = 0; offset < duration; offset++) {
                if (!isMinuteFree(period, startMinute + offset)) {
                    blockFree = false;
                    break;
                }
            }

            if (blockFree) {
                return startMinute;
            }
        }

        return -1;
    }

    public boolean makeAppointment(int startPeriod, int endPeriod, int duration) {
        for (int period = startPeriod; period <= endPeriod; period++) {
            int startMinute = findFreeBlock(period, duration);

            if (startMinute != -1) {
                reserveBlock(period, startMinute, duration);
                return true;
            }
        }

        return false;
    }
}

public class MyProgram {
    public static void main(String[] args) {
        AppointmentBook book = new AppointmentBook(6, 60);
        System.out.println(book.makeAppointment(1, 3, 20));
        System.out.println(book.makeAppointment(2, 4, 60));
        System.out.println(book.makeAppointment(1, 6, 60));
    }
}
MyProgram.main(null);

Key Takeaways

In this question, I needed to carefully manage a nested loop search over a range of minutes within a period to find a contiguous free block. For Part A, the key insight was computing maxStart = 60 - duration to avoid going out of bounds, then using a boolean flag with an early break to check each candidate window efficiently. For Part B, I reused findFreeBlock as a helper and only called reserveBlock after confirming availability, making sure to return true immediately on the first successful booking and false only after exhausting all periods. Overall, this problem tested my understanding of nested iteration, helper method composition, and short-circuit control flow.

2023 FRQ 1