From de869bc2307b52522484cf84d5024f112f28ea66 Mon Sep 17 00:00:00 2001 From: kubasync-clanker Date: Tue, 10 Feb 2026 18:51:21 +0000 Subject: [PATCH] clanker: veet-hard-problems (run) --- problems/hard/shortest-path/solution.py | 40 ++++++++++++ problems/hard/shortest-path/tests.py | 75 ++++++++++++++++++++++ problems/hard/task-scheduler/solution.py | 45 ++++++++++++++ problems/hard/task-scheduler/tests.py | 79 ++++++++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 problems/hard/shortest-path/solution.py create mode 100644 problems/hard/shortest-path/tests.py create mode 100644 problems/hard/task-scheduler/solution.py create mode 100644 problems/hard/task-scheduler/tests.py diff --git a/problems/hard/shortest-path/solution.py b/problems/hard/shortest-path/solution.py new file mode 100644 index 0000000..1e9f51a --- /dev/null +++ b/problems/hard/shortest-path/solution.py @@ -0,0 +1,40 @@ +""" +Shortest Path + +You're building a navigation system for a logistics company. Given a +network of cities connected by weighted roads, implement a shortest-path +finder that computes the minimum-cost route between two cities using +Dijkstra's algorithm. The system must also detect when no route exists. + +Example 1: + Input: edges = [("A", "B", 4), ("A", "C", 2), ("C", "B", 1), ("B", "D", 5), ("C", "D", 8)], start = "A", end = "D" + Output: (7, ["A", "C", "B", "D"]) + Explanation: A->C (2) + C->B (1) + B->D (5) = 7, cheaper than A->C->D (10) or A->B->D (9). + +Example 2: + Input: edges = [("X", "Y", 3)], start = "Y", end = "X" + Output: (-1, []) + Explanation: No path from Y to X because the edge is one-directional. + +Example 3: + Input: edges = [("A", "B", 1), ("B", "C", 2), ("A", "C", 10)], start = "A", end = "C" + Output: (3, ["A", "B", "C"]) + Explanation: Going through B costs 3, which beats the direct edge of 10. + +Constraints: + - Edges are directed: (source, destination, weight) + - All weights are positive integers (>= 1) + - No duplicate edges (same source and destination) + - Node names are non-empty strings + - Return (-1, []) if no path exists + - Return (0, [start]) if start == end + - If multiple shortest paths exist, return any one of them + - The path list includes both start and end nodes +""" + + +def shortest_path( + edges: list[tuple[str, str, int]], start: str, end: str +) -> tuple[int, list[str]]: + """Return (cost, path) for shortest path, or (-1, []) if unreachable.""" + pass # Your implementation here diff --git a/problems/hard/shortest-path/tests.py b/problems/hard/shortest-path/tests.py new file mode 100644 index 0000000..705f03a --- /dev/null +++ b/problems/hard/shortest-path/tests.py @@ -0,0 +1,75 @@ +"""Tests for shortest-path.""" +import pytest +from solution import shortest_path + + +class TestBasicCases: + """Test basic functionality with typical inputs.""" + + def test_example_one(self): + """Test first example from problem description.""" + edges = [("A", "B", 4), ("A", "C", 2), ("C", "B", 1), ("B", "D", 5), ("C", "D", 8)] + assert shortest_path(edges, "A", "D") == (7, ["A", "C", "B", "D"]) + + def test_example_two(self): + """Test second example with no reverse path.""" + edges = [("X", "Y", 3)] + assert shortest_path(edges, "Y", "X") == (-1, []) + + def test_example_three(self): + """Test indirect path cheaper than direct edge.""" + edges = [("A", "B", 1), ("B", "C", 2), ("A", "C", 10)] + assert shortest_path(edges, "A", "C") == (3, ["A", "B", "C"]) + + def test_direct_edge_is_shortest(self): + """Test when the direct edge is the cheapest route.""" + edges = [("A", "B", 1), ("A", "C", 5), ("C", "B", 5)] + assert shortest_path(edges, "A", "B") == (1, ["A", "B"]) + + +class TestEdgeCases: + """Test edge cases and boundary conditions.""" + + def test_start_equals_end(self): + """Test when start and end are the same node.""" + edges = [("A", "B", 1)] + assert shortest_path(edges, "A", "A") == (0, ["A"]) + + def test_no_edges(self): + """Test with empty edge list and different start/end.""" + assert shortest_path([], "A", "B") == (-1, []) + + def test_single_edge_path(self): + """Test path that is a single edge.""" + edges = [("A", "B", 7)] + assert shortest_path(edges, "A", "B") == (7, ["A", "B"]) + + def test_long_chain(self): + """Test shortest path through a long chain of nodes.""" + edges = [("A", "B", 1), ("B", "C", 1), ("C", "D", 1), ("D", "E", 1)] + assert shortest_path(edges, "A", "E") == (4, ["A", "B", "C", "D", "E"]) + + def test_disconnected_graph(self): + """Test with disconnected components.""" + edges = [("A", "B", 1), ("C", "D", 1)] + assert shortest_path(edges, "A", "D") == (-1, []) + + def test_multiple_paths_picks_cheapest(self): + """Test graph with many paths to verify optimal is chosen.""" + edges = [ + ("S", "A", 10), ("S", "B", 3), ("B", "A", 1), + ("A", "T", 2), ("B", "T", 20), + ] + cost, path = shortest_path(edges, "S", "T") + assert cost == 6 + assert path[0] == "S" and path[-1] == "T" + + def test_large_weights(self): + """Test with large edge weights.""" + edges = [("A", "B", 1000000), ("B", "C", 1000000)] + assert shortest_path(edges, "A", "C") == (2000000, ["A", "B", "C"]) + + def test_end_node_not_in_graph(self): + """Test when end node has no edges at all.""" + edges = [("A", "B", 1), ("B", "C", 2)] + assert shortest_path(edges, "A", "Z") == (-1, []) diff --git a/problems/hard/task-scheduler/solution.py b/problems/hard/task-scheduler/solution.py new file mode 100644 index 0000000..2b24fa3 --- /dev/null +++ b/problems/hard/task-scheduler/solution.py @@ -0,0 +1,45 @@ +""" +Task Scheduler + +You're building a CI/CD pipeline orchestrator. Given a set of build tasks +with durations and dependency requirements, determine the minimum total +time to complete all tasks when independent tasks can run in parallel. +Also detect if the dependency graph contains a cycle (making the build +impossible). + +Each task is represented as (name, duration, dependencies) where +dependencies is a list of task names that must complete before this +task can start. + +Example 1: + Input: tasks = [("compile", 3, []), ("test", 5, ["compile"]), ("lint", 2, []), ("deploy", 1, ["test", "lint"])] + Output: (9, ["compile", "test", "deploy"]) + Explanation: compile(3) -> test(5) -> deploy(1) = 9. lint(2) runs in parallel and finishes before deploy starts. The critical path is compile -> test -> deploy. + +Example 2: + Input: tasks = [("A", 2, ["B"]), ("B", 3, ["A"])] + Output: (-1, []) + Explanation: Circular dependency between A and B makes execution impossible. + +Example 3: + Input: tasks = [("X", 4, []), ("Y", 4, []), ("Z", 1, ["X", "Y"])] + Output: (5, ["X", "Z"]) + Explanation: X and Y run in parallel (both take 4). Z waits for both, then takes 1. Critical path is X(4) -> Z(1) = 5 (or equivalently Y -> Z). Return either. + +Constraints: + - Task names are unique non-empty strings + - Durations are positive integers (>= 1) + - Dependencies reference other task names in the list + - Return (-1, []) if a cycle exists + - The critical path is the longest path through the dependency graph + - If multiple critical paths have the same length, return any one + - The critical path list is ordered from first task to last + - All tasks must be completed; the answer is the makespan +""" + + +def schedule_tasks( + tasks: list[tuple[str, int, list[str]]], +) -> tuple[int, list[str]]: + """Return (min_total_time, critical_path) or (-1, []) if cycle exists.""" + pass # Your implementation here diff --git a/problems/hard/task-scheduler/tests.py b/problems/hard/task-scheduler/tests.py new file mode 100644 index 0000000..210c9aa --- /dev/null +++ b/problems/hard/task-scheduler/tests.py @@ -0,0 +1,79 @@ +"""Tests for task-scheduler.""" +import pytest +from solution import schedule_tasks + + +class TestBasicCases: + """Test basic functionality with typical inputs.""" + + def test_example_one(self): + """Test first example from problem description.""" + tasks = [("compile", 3, []), ("test", 5, ["compile"]), ("lint", 2, []), ("deploy", 1, ["test", "lint"])] + assert schedule_tasks(tasks) == (9, ["compile", "test", "deploy"]) + + def test_example_two_cycle(self): + """Test second example with circular dependency.""" + tasks = [("A", 2, ["B"]), ("B", 3, ["A"])] + assert schedule_tasks(tasks) == (-1, []) + + def test_example_three_parallel(self): + """Test parallel tasks with shared dependency.""" + tasks = [("X", 4, []), ("Y", 4, []), ("Z", 1, ["X", "Y"])] + cost, path = schedule_tasks(tasks) + assert cost == 5 + assert path[0] in ("X", "Y") and path[-1] == "Z" + + def test_linear_chain(self): + """Test simple linear dependency chain.""" + tasks = [("A", 2, []), ("B", 3, ["A"]), ("C", 4, ["B"])] + assert schedule_tasks(tasks) == (9, ["A", "B", "C"]) + + +class TestEdgeCases: + """Test edge cases and boundary conditions.""" + + def test_single_task(self): + """Test with a single task and no dependencies.""" + tasks = [("solo", 5, [])] + assert schedule_tasks(tasks) == (5, ["solo"]) + + def test_all_independent(self): + """Test all tasks run in parallel with no dependencies.""" + tasks = [("A", 3, []), ("B", 7, []), ("C", 5, [])] + cost, path = schedule_tasks(tasks) + assert cost == 7 + assert path == ["B"] + + def test_diamond_dependency(self): + """Test diamond-shaped dependency graph.""" + tasks = [("A", 1, []), ("B", 5, ["A"]), ("C", 2, ["A"]), ("D", 1, ["B", "C"])] + assert schedule_tasks(tasks) == (7, ["A", "B", "D"]) + + def test_three_node_cycle(self): + """Test cycle involving three nodes.""" + tasks = [("A", 1, ["C"]), ("B", 1, ["A"]), ("C", 1, ["B"])] + assert schedule_tasks(tasks) == (-1, []) + + def test_wide_fan_in(self): + """Test many tasks feeding into one final task.""" + tasks = [("A", 1, []), ("B", 2, []), ("C", 3, []), ("D", 4, []), ("final", 1, ["A", "B", "C", "D"])] + assert schedule_tasks(tasks) == (5, ["D", "final"]) + + def test_deep_chain_with_parallel_branch(self): + """Test long chain alongside a shorter parallel branch.""" + tasks = [ + ("A", 1, []), ("B", 1, ["A"]), ("C", 1, ["B"]), ("D", 1, ["C"]), + ("shortcut", 2, []), + ("end", 1, ["D", "shortcut"]), + ] + assert schedule_tasks(tasks) == (5, ["A", "B", "C", "D", "end"]) + + def test_partial_cycle_with_valid_tasks(self): + """Test graph where some tasks form a cycle but others don't.""" + tasks = [("A", 1, []), ("B", 2, ["C"]), ("C", 3, ["B"])] + assert schedule_tasks(tasks) == (-1, []) + + def test_large_durations(self): + """Test with large task durations.""" + tasks = [("A", 1000000, []), ("B", 1000000, ["A"])] + assert schedule_tasks(tasks) == (2000000, ["A", "B"])