E - アルバイトのシフト割り当て / Part-Time Job Shift Assignment Editorial by admin
Claude 4.5 OpusOverview
This is a problem of matching time slots with part-time job candidates, which is a classic problem of finding the maximum bipartite matching.
Analysis
Problem Reformulation
This problem can be organized as follows: - \(N\) “time slot” nodes on the left side - \(M\) “candidate” nodes on the right side - If candidate \(j\) can work during time slot \(i\), draw an edge \((i, j)\)
In this graph, the problem becomes selecting as many edges as possible such that each node is connected to at most one edge. This is exactly the maximum bipartite matching problem.
Issues with the Naive Approach
If we try all possible assignment patterns, we would need to examine up to \(O(M^N)\) combinations in the worst case, which is far too slow for \(N, M \leq 100\).
Solution
Efficient algorithms exist for maximum bipartite matching: - Hopcroft-Karp algorithm: Computes the result in \(O(E\sqrt{V})\) - Hungarian algorithm and simple augmenting path search can also be used
Algorithm
Basic Concept of Bipartite Matching
We find augmenting paths and increase the matching one by one.
An augmenting path is a path that starts with an edge “not in the matching,” alternates between edges “in the matching” and edges “not in the matching,” and ends with an edge “not in the matching.”
Flow of the Hopcroft-Karp Algorithm
- BFS Phase: Starting from unmatched left-side nodes, find the length of the shortest augmenting path
- DFS Phase: Process all augmenting paths of the found shortest length simultaneously and update the matching
- Repeat until no more augmenting paths are found
Example: N=3, M=3
Time slot 1 → Candidates 1, 2
Time slot 2 → Candidate 1
Time slot 3 → Candidates 2, 3
Maximum matching:
Time slot 1 ↔ Candidate 2
Time slot 2 ↔ Candidate 1
Time slot 3 ↔ Candidate 3
→ Answer is 3
Complexity
Time complexity: \(O(E\sqrt{V})\)
- \(V = N + M\) (number of vertices), \(E = \sum K_i\) (number of edges)
- In the worst case \(E = NM\), \(V = N + M\), so \(O(NM\sqrt{N+M})\)
- Fast enough for \(N, M \leq 100\)
Space complexity: \(O(N + M + E)\)
- \(O(E)\) for storing the adjacency list
- \(O(N + M)\) for the matching arrays
Implementation Notes
Conversion to 0-indexed: Since candidate numbers start from \(1\) in the input, convert to 0-indexed using
c - 1Managing matching with two arrays:
match_left[i]: The candidate assigned to time slot \(i\) (-1 if unassigned)match_right[j]: The time slot assigned to candidate \(j\) (-1 if unassigned)
Computing distances with BFS: Calculate distances from unmatched time slots, then efficiently search for augmenting paths with DFS
Pruning in DFS: Set
dist[u] = infto avoid revisiting the same nodeSource Code
def main():
import sys
from collections import deque
input_data = sys.stdin.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
# Build bipartite graph
# Left side: time slots (0 to N-1)
# Right side: candidates (0 to M-1)
# For each time slot, store list of candidates who can work
adj = [[] for _ in range(N)]
for i in range(N):
K = int(input_data[idx]); idx += 1
for _ in range(K):
c = int(input_data[idx]) - 1; idx += 1 # 0-indexed
adj[i].append(c)
# Maximum bipartite matching using Hopcroft-Karp algorithm
# match_left[i] = candidate matched to time slot i (-1 if unmatched)
# match_right[j] = time slot matched to candidate j (-1 if unmatched)
match_left = [-1] * N
match_right = [-1] * M
def bfs():
dist = [float('inf')] * N
queue = deque()
for i in range(N):
if match_left[i] == -1:
dist[i] = 0
queue.append(i)
found = False
while queue:
u = queue.popleft()
for v in adj[u]:
next_u = match_right[v]
if next_u == -1:
found = True
elif dist[next_u] == float('inf'):
dist[next_u] = dist[u] + 1
queue.append(next_u)
return found, dist
def dfs(u, dist):
for v in adj[u]:
next_u = match_right[v]
if next_u == -1 or (dist[next_u] == dist[u] + 1 and dfs(next_u, dist)):
match_left[u] = v
match_right[v] = u
return True
dist[u] = float('inf')
return False
matching = 0
while True:
found, dist = bfs()
if not found:
break
for i in range(N):
if match_left[i] == -1:
if dfs(i, dist):
matching += 1
print(matching)
if __name__ == "__main__":
main()
This editorial was generated by claude4.5opus.
posted:
last update: