The following code is my solution to the Closest Pick problem from Codejam 2021.
You are entering a raffle for a lifetime supply of pancakes. N tickets have already been sold. Each ticket contains a single integer between 1 and K, inclusive. Different tickets are allowed to contain the same integer. You know exactly which numbers are on all of the tickets already sold and would like to maximize your odds of winning by purchasing two tickets (possibly with the same integer on them). You are allowed to choose which integers between 1 and K, inclusive, are on the two tickets.
You know you are the last customer, so after you purchase your tickets, no more tickets will be purchased. Then, an integer c between 1 and K, inclusive, is chosen uniformly at random. If one of your tickets is strictly closer to c than all other tickets or if both of your tickets are the same distance to c and strictly closer than all other tickets, then you win the raffle. Otherwise, you do not win the raffle.
Given the integers on the N tickets purchased so far, what is the maximum probability of winning the raffle you can achieve by choosing the integers on your two tickets optimally?
This is an easy problem, and we can solve it by sorting all ticket numbers and finding all number gaps between them. Then we can buy two tickets either to cover the largest gap or half of the two largest gaps.
The code below is correct, i.e. it gets two green checkmarks.
I'd appreciate any feedback!
#include <stdio.h>
#include <stdlib.h>
#define MIN_TICKET 1
static int cmp_long(const void *num1, const void *num2) {
return *(const long *)num1 - *(const long *)num2;
}
static double solve(long *tickets, size_t n_tickets, long max_ticket) {
qsort(tickets, n_tickets, sizeof(tickets[0]), cmp_long);
// Sizes of ticket gaps that we can "cover" by being closer than others
long gaps[n_tickets + 1];
// Max total number of tickets that we can cover
long max_gap_size = 0;
// Size of the leftmost ticket gap
gaps[0] = tickets[0] - MIN_TICKET;
// Size of the rightmost ticket gap
gaps[n_tickets] = max_ticket - tickets[n_tickets-1];
for (int i = 1; i < n_tickets; i++) {
long gap_size = tickets[i] - tickets[i-1] - 1;
// Number of tickets that we can cover by buying a ticket at one of the
// gap boundaries (divide the size by 2 and round up).
gaps[i] = (gap_size + 1) / 2;
// Alternatively, we can buy two tickets at each boundary of a large
// gap.
if (gap_size > max_gap_size) {
max_gap_size = gap_size;
}
}
qsort(gaps, n_tickets + 1, sizeof(gaps[0]), cmp_long);
// Total size of our two largest options
long top_two_gap_sizes = gaps[n_tickets - 1] + gaps[n_tickets];
// Choose between covering one large gap or two smaller gaps.
if (top_two_gap_sizes > max_gap_size) {
max_gap_size = top_two_gap_sizes;
}
return (double) max_gap_size / max_ticket;
}
int main(void) {
size_t n_tests = 0;
scanf("%lu", &n_tests);
for (int case_num = 1; case_num <= n_tests; case_num++) {
size_t n_tickets;
long max_ticket;
scanf("%lu %ld", &n_tickets, &max_ticket);
long tickets[n_tickets];
for (int i = 0; i < n_tickets; i++) {
scanf("%ld", &tickets[i]);
}
double prob = solve(tickets, n_tickets, max_ticket);
printf("Case #%d: %.6f\n", case_num, prob);
}
}
Example output
$ cat tests.txt
4
3 10
1 3 7
4 10
4 1 7 3
4 3
1 2 3 2
4 4
1 2 4 2
$ ./solution < tests.txt
Case #1: 0.500000
Case #2: 0.400000
Case #3: 0.000000
Case #4: 0.250000
tickets
andmax_ticket
want to be unsigned? Also, once we win all those pancakes, we will have many opportunities to solve burnt pancake problems! \$\endgroup\$max_gap_size
in a loop? Afterqsort(gap
you'd get it for free. \$\endgroup\$max_gap_size
does not include the leftmost and rightmost gaps, because those can be covered completely by a single ticket. \$\endgroup\$