Kattis of the Week
4 September 2024
Since I am no longer teaching CS2040 (Data Structures and Algorithms) as an undergraduate teaching assistant in NUS, I thought it would be nice to dump some additional Kattis problems here (initially for my students) that I usually call as KOTW (stands for the article title). Future CS2040 students might find this article useful.
Little warning that these problems are almost sorted by difficulty.
T2: Sorting
Missing Gnomes
Literally the last question of this tutorial (first permutation thingy)Height Ordering
Simulate insertion sortMessy lists
Suppose is the sorted version of , how many values of i such thatS[i] != A[i]
?
Pivot (hard problem)
You probably see this question very often in your VisuAlgo quiz. If an element is a possible pivot, all elements to its left must be smaller and all elements to its right must be larger. To keep track of this, you can use an array to keep track of the minimum/maximum so far as you iterate through array from the right/left direction. This technique is also known as the running min/maxT3: Lists, Stacks, Queues
Circuit Math
Now we deal with postfix expressions! Same idea, but instead you can use one stack: if you see an operator, pop the operator and pop the two operands, and push the evaluation result, rinse and repeatBracket Sequence (slightly harder)
Now we deal with infix expressions! Again same idea, with an extra toggle whether you should add everything or multiply everything. Read tokens until a you see a close bracketFind my Family (hard but useful)
My fellow ex-tutor Eric Leow has provided the entire explanation for you, here we introduce you to a special kind of stack, the monotonic stack!Bungee Builder
Now that you've learnt about monotonic stacks, try this out :)T4: Hashing
I Wanna Be The Very Best
Have a hashset that contains top K attacks, top K defense, and top K health. Report the length of the hashset (which should at most )CD
Just populate the two sets A and B and report the size of the intersection. You can use a for loop to do this :)Variable Names
Have a hashtable of(number: variable_index)
and simulate the process until the number reaches , if the number isn't taken yet it shouldn't be an existing key inside the hashtable
Quickscope (fun + hard? combo?)
TLDR: stack-of-hashmaps and hashmap-of-stacksThis is quite hard, but gives an idea of a bi-map where you store the mapping for both directions: one map tells which levels of nesting a variable has and one map tells what variables are on a particular nesting level.
Suppose we have a stack of hashmaps called and another hashmap called . At any point of time, let be the hashmap on the top of .
Our goal is such that for each hashmap in , it stores the variables in the corresponding nesting level and their type as a key-value pair. As for , the keys are the variables and the values are stacks of the nesting levels this variable is currently used.
- If you detect a new level of nesting being added (i.e. detect a
"{"
), push another empty hashmap to (this is the new ). This ensures that the hashmap on top of the stack refers to the current nesting level you're at - If you see a
DECLARE x y
, add as the key of and as the value. If already exists beforehand as a key in , you know it's aMULTIPLE DECLARATION
, print that and terminate the code immediately. Otherwise, you also need to add the nesting level into accordingly (check if is already a key in and then handle the addition accordingly) - If you see a
TYPEOF x
, then you know that we can always look at the last element ofT[x]
, say it's , and reportS[d][x]
(the stack must be an arraylist so we can still access easily) orUNDECLARED
ifT[x]
is empty - If you detect the end of the nesting (i.e. a
"}"
), we need to do two things in this order: ensure that you have popped this nesting level from on all variables contained in . After that, you safely pop out of ( changes to the current top of )
Here's an example to visualize what's going on. Suppose we have this as the input
DECLARE a int
DECLARE b float
{
DECLARE b int
DECLARE c int
}
DECLARE d double
then our looks like [{a: int, b: float, d: double}, {b: int, c: int}]
and our looks like {a: [0], b: [0, 1], c: [1], d: [0]}
T5: PQ
Continuous Median
One of my favourite Leetcode problems. The idea is to have one max heap to store the first half of the "array" and then one min heap to store the latter half. For every iteration, insert to one of the heaps and balance the sizes whenever necessary (e.g. ensure that max heap size is and min heap size if ). When reporting the median, check whether you need to peek the max heap or do you have to take the average of both heap peeks (depends on whether is currently odd or even)!Koffínhraði
Quite a big brain problem, thought I'd like to share with you guys. The idea is to sort the tasks by the DEADLINE because obviously you want to finish things due earlier first. Next, have a max heap to store the task times and a variable to keep track of the sum of the elements in the max heap so far. As you go through the sorted task list, we need to ensure that this sum does not exceed the deadline time of the task you're dealing with. Otherwise, until the sum is before the deadline, keep taking the task with the largest time (thus a max heap), half it, and put it back into the heap. This ensures that after completely processing the task, the sum of the task time so far will never exceed the deadline of the last task.One more thing is that we take the task with the largest time because the amount of time saved by halving would be the most when performed on this particular task. For example, 5 -> 2 saves 3 seconds but 100 -> 50 saves 50 seconds!
Pseudocode would be something like this:
pq = max heap initially empty
sum = 0
ans = 0
sort the tasks by increasing deadline
for each task in the sorted task list:
pq.push(task_time)
sum += task_time
while sum > task_deadline:
t = popped task_time from pq
pq.push(t/2)
sum = sum - t + t/2
ans += 1
output ans
Stock Prices
Have a max heap for the buy prices and min heap for sell prices, keep polling from both queues accordingly using a while loop!T6: UFDS
Union-Find
It's literally union find without any modification, like, at all.Reachable Roads
If and are connected by an edge, just merge and in the UFDS, reachable if they belong to the same setGraduation
Merge accordingly and find the number of disjoint sets leftBus Numbers
UFDS from 1 to 1000, keep track of min and max of each setTildes
Keep track of size, then answer the query accordinglyT7: BST
Continuous Median (again!)
Yes, again you can use BST for this instead of priority queue, just repeated use the select method from AVL tree as you insert elements into itGalactic Collegiate Programming Contest
Store all the teams into an AVL tree, update the points by deleting the node corresponding to the particular team and then reinsert the updated node corresponding to that same team. Note that you need to use therank
function in AVL tree!
99 Problems
You can use the successor method in AVL trees or just use the TreeSet/TreeMap methods such asfloorKey
or higherKey
! The rest is straightforward based on the instructions
Classrooms (hard problem?)
See interval scheduling on Wikipedia for the official term for this problem but you use an AVL tree to store the compatible intervals so far!T8: Graph DS and Trav
Cut the Negativity
Basic conversion from adjacency matrix to edge list, a simple nested for loop will doMap of Sweden
Most graph problems in Kattis involve 2D grids, so let's get familiar with making the adjacency list for this one, and then we can also represent each cell as one element of an UFDS on top of the adjacency list. Then for each query, it will be just the combination of enumerating the neighbours of the adjacency list and the merge part of UFDSRunning MoM
Let's apply cycle checking for this one. Report 'safe' is there is a cycle involving the city in query, 'trapped' otherwise.Námsleið
Use Kahn's algorithm to keep track of the maximum distance between a node and any node with indegree 0. This can be done by carrying the current depth into the queue (store(node, depth)
instead of just node
, then you can update into (next_node, depth+1)
). Finally, use a hashmap to keep track which modules to take for each depth value, assuming the graph is a DAG.
Pachinko Probability (slightly hard)
Interesting problem that uses toposort to count the number of paths from any vertex with indegree 0 to those with outdegree 0, then among those paths how many land on the squared vertices (if you're using Kahn's algorithm, accumulate the number of paths in reverse topological order)T9: MST
Minimum Spanning Tree
Basic MST problem, given input output MST weight and all the edgesFreckles
Simply the MST costNature Reserve
Prim's, this tutorial's last question (the power plant thingy)Treehouses
Run Kruskal's with all the first houses merged on the same set in the UFDSGrid MST (very hard, proceed with caution)
Kruskal's but you need a way to reduce the graph using BFS since without it is too big thus TLE, more details are omitted in this articleT10: SSSP P1
Button Bashing
First question of the tutorial (unlocking the lock), same idea!Fire
Same as the fire tutorial question!Fire!
Same as the fire tutorial question!Slikar
Same as the fire tutorial question! Had enough dejavus?Ocean Currents (useful!)
I'd like to introduce you guys to a variant of BFS callled 0-1 BFS. Assuming you managed to populate the graph, you will see that the edge weights are either 0 or 1. Instead of using a queue to perform the BFS, use a double-ended queue! This is because you have to either append the next state to the front of the queue or the back of the queue, depending on the edge weight, the front if it's 0 and the back if it's 1. This ensures that the double-ended queue will preserve the ordering of the distances taken so far (remains sorted)!Get Shorty
Similar to the money question, use negative log transformation and then just use Dijkstra!Single source shortest path, negative weights
Interesting SP problem, even with negative cycles doesn't mean the shortest path to any vertex cannot be found since it may be outside the negative cycle itselfHow to tackle: Run bellman ford and compare the -th iteration with the next iterations (at most more iterations). If the value doesn't change, that's the SP already. Otherwise, the vertex is part of a negative cycle. You can use a while loop to keep track of which vertices has its value changed but not marked as part of negative cycle.
T11: SSSP P2 + APSP
Fend Off Titan
Just a regular Dijkstra but instead of storing(sp_estimate, node)
in the priority queue, store (num_of_titans, num_of_shamans, sp_estimate, node)
instead, since we are comparing the values in this order
Invasion
Also another variant of Dijkstra, but keep track of what's unsafe until thesp_estimate
reaches !
Tima goes to Xentopia
The idea is to clone the graph and run regular Dijkstra, very similar to that of Finals 20/21 Sem 2 Q19Real Manhattan Distance (hard problem)
The hard part is how to construct the adjacency list since it involves linear algebra to some extent. Otherwise, it's just DijkstraVisuAlgo Online Quiz
A variant of Dijkstra algorithm where instead of storing just a single integer on the D array, you store a pair(sp_estimate, num_of_paths)
. Make sure you managed to update the value of num_of_paths
accordingly, whether D[node] == curr_sp_estimate
or D[node] > curr_sp_estimate
(to which you have to reset it to 0)