Hi everyone,
I recently found a USACO problem that fits in really well with the material from Chapter 4. I included in the book one of my favourite USACO problems, but there are so many more to look at.
… like this one! It’s a graphs problem, and you can use breadth-first search to solve it.
(True but pointless story: USACO problems almost all deal with cows. So I tried to sneak the word encownter into the book. I got it past a few people but eventually it was caught. I pushed back – gotta know what’s worth fighting for, eh? – but, nope, no go. Next time I have to slip this kind of thing into the book riiiiight before it goes to print :) )
The problem is called MooTube, and it’s from the Silver January 2018 contest.
Please read the problem. Then close my blog! Yeah, for real… close it. You can do this. Keep in mind what you learned from the Book Translation problem that we worked through in Chapter 4. For those who don’t have access to the book: don’t worry, the code for the book is all here in this code archive.
You can start with the code for Book Translation, and modify it to arrive at a solution to MooTube. In fact that’s just what I did!
Don’t worry if you get stuck here. I’ve provided a solution below, so you’re not going to leave this problem without being able to directly connect it to what you learned in the book. Getting stuck can be a very productive learning experience, because it zones you in on exactly what you need to learn next. So, give it a good try, and then, once your ready, see below for my solution.
Here’s the code I came up with.
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_VIDEOS 5000
typedef struct edge {
int to_video, relevance;
struct edge *next;
} edge;
typedef int board[MAX_VIDEOS + 1];
typedef int positions[MAX_VIDEOS + 1];
// Return 1 if to_video is added as a new position, 0 otherwise.
int add_position(int from_video, int to_video,
positions new_positions, int *num_new_positions,
board min_moves) {
if (min_moves[to_video] == -1) {
min_moves[to_video] = 1 + min_moves[from_video];
new_positions[*num_new_positions] = to_video;
(*num_new_positions)++;
return 1;
}
return 0;
}
int solve(edge *adj_list[], int relevance, int video, int num_videos) {
static board min_moves;
static positions cur_positions, new_positions;
int num_cur_positions, num_new_positions;
int i, from_video;
int total = 0;
edge *e;
for (i = 1; i <= num_videos; i++) {
min_moves[i] = -1;
}
min_moves[video] = 0;
cur_positions[0] = video;
num_cur_positions = 1;
while (num_cur_positions > 0) {
num_new_positions = 0;
for (i = 0; i < num_cur_positions; i++) {
from_video = cur_positions[i];
e = adj_list[from_video];
while (e) {
if (e->relevance >= relevance) {
if (add_position(from_video, e->to_video,
new_positions, &num_new_positions, min_moves))
total++;
}
e = e->next;
}
}
num_cur_positions = num_new_positions;
for (i = 0; i < num_cur_positions; i++)
cur_positions[i] = new_positions[i];
}
return total;
}
int main(void) {
static edge *adj_list[MAX_VIDEOS + 1] = {NULL};
int i, num_videos, num_queries, from_index, to_index, relevance, video, result;
edge *e;
freopen("mootube.in", "r", stdin);
freopen("mootube.out", "w", stdout);
scanf("%d%d", &num_videos, &num_queries);
for (i = 0; i < num_videos - 1 ; i++) {
scanf("%d%d%d", &from_index, &to_index, &relevance);
e = malloc(sizeof(edge));
if (e == NULL) {
fprintf(stderr, "malloc error\n");
exit(1);
}
e->to_video = to_index;
e->relevance = relevance;
e->next = adj_list[from_index];
adj_list[from_index] = e;
e = malloc(sizeof(edge));
if (e == NULL) {
fprintf(stderr, "malloc error\n");
exit(1);
}
e->to_video = from_index;
e->relevance = relevance;
e->next = adj_list[to_index];
adj_list[to_index] = e;
}
for (i = 0; i < num_queries; i++) {
scanf("%d%d", &relevance, &video);
result = solve(adj_list, relevance, video, num_videos);
printf("%d\n", result);
}
return 0;
}
Just a few notes here that I’d like to highlight as differences from Book Translation:
The add_position
function (17) returns whether it succeeded in adding the position. This is helpful because we ultimately need to know how many positions (representing videos) were reached.
This code continues to find the minimum number of moves to each video. But we don’t really care about the number of moves, as long as a video is reachable. If I were solving this problem from scratch, rather than adapting more general breadth-first code, I probably wouldn’t have kept track of the number of moves.
Rather than add every reachable video, we only want to consider adding videos that meet the relevance threshold. To do this, we check the relevance of each video (50) before calling add_position
.
This USACO problem requires that we read from a file and write to a different file; unlike many other problems, we’re not using standard input and standard output here. In my code, I use the freopen
function to point standard input/output at these files, so that I can keep using scanf
and printf
as normal.
I hope you were able to make a lot of practice here! For more, check out the little collection of exercises that I’ve posted.
Written on January 20th, 2021 by Daniel Zingaro