Graph Algorithms — From Basics to Advanced

Правка en1, от ripc, 2026-09-06 11:50:23

Graph Algorithms — From Basics to Advanced

Graphs are one of the most important topics in competitive programming.

Many problems that look completely different on the surface can actually be modeled as a graph:

  • Cities connected by roads
  • Computers connected by networks
  • People connected through relationships
  • Courses connected by prerequisites
  • States connected by transitions
  • Cells connected in a grid
  • Websites connected by links

Once we recognize the vertices and edges, the problem often becomes much easier.

This blog is a complete guide to graph algorithms for competitive programming, especially useful for Codeforces.


1. What Is a Graph?

A graph consists of:

  • Vertices (Nodes) — the objects
  • Edges — connections between objects

We usually represent a graph as:

$$$ G = (V,E) $$$

where:

  • (V) = set of vertices
  • (E) = set of edges

For example:

1 ----- 2
|       |
|       |
3 ----- 4

Here:

Vertices = {1, 2, 3, 4}

Edges = {
    (1,2),
    (1,3),
    (2,4),
    (3,4)
}

2. Types of Graphs

Before choosing an algorithm, first identify what type of graph you have.

Undirected Graph

An edge works in both directions.

1 ----- 2

If we can go:

1 → 2

then we can also go:

2 → 1

Directed Graph

An edge has a direction.

1 -----> 2

We can go:

1 → 2

but not necessarily:

2 → 1

Weighted Graph

Every edge has a cost.

1 --5-- 2

The cost of going from 1 to 2 is 5.


Unweighted Graph

Edges don't have explicit weights.

1 ----- 2

Usually, every edge can be considered to have weight 1.


Cyclic Graph

A graph containing a cycle.

1 ---- 2
|      |
|      |
4 ---- 3

We can start at 1 and return to 1.


Acyclic Graph

A graph without cycles.

A directed acyclic graph is called a:

DAG — Directed Acyclic Graph


3. Important Terminology

Degree

For an undirected graph:

degree(v) = number of edges connected to v

For directed graphs:

  • indegree = incoming edges
  • outdegree = outgoing edges

Example:

1 → 2 → 3
    ↑
    |
    4

For node 2:

indegree = 2
outdegree = 1

4. Path

A path is a sequence of vertices connected by edges.

1 → 2 → 5 → 7

Path length in an unweighted graph:

number of edges

Here:

length = 3

5. Cycle

A cycle is a path that starts and ends at the same vertex.

1 → 2 → 3 → 1

Cycle detection is one of the most common graph problems on Codeforces.


6. Connected Components

Consider:

1 -- 2      4 -- 5
|           |
3           6

There are two connected components:

Component 1 = {1,2,3}

Component 2 = {4,5,6}

DFS or BFS can find connected components in:

$$$ O(V+E) $$$

7. Graph Representation

There are three common ways to represent graphs.


7.1 Adjacency Matrix

For:

1 ----- 2
|
|
3

we can use:

    1 2 3
1   0 1 1
2   1 0 0
3   1 0 0

Complexity

Space:

$$$ O(V^2) $$$

Checking whether an edge exists:

$$$ O(1) $$$

Iterating through all neighbors:

$$$ O(V) $$$

When to use?

Usually when:

V is small

For example:

V ≤ 500

depending on memory limits.


7.2 Adjacency List

This is the most common representation in competitive programming.

For:

1 -- 2
|    |
3 -- 4

we can store:

1: 2, 3
2: 1, 4
3: 1, 4
4: 2, 3

In C++:

vector<vector<int>> graph(n + 1);

graph[u].push_back(v);
graph[v].push_back(u);

For a directed graph:

graph[u].push_back(v);

Complexity

Space:

$$$ O(V+E) $$$

This is generally the best representation for Codeforces graph problems.


7.3 Edge List

Store edges directly:

vector<pair<int,int>> edges;

For weighted graphs:

struct Edge {
    int u, v, w;
};

Useful for algorithms such as:

  • Kruskal
  • Bellman-Ford
  • Some offline graph problems

8. Graph Representation Comparison

Representation Space Check Edge Find Neighbors Best Use
Adjacency Matrix O(V²) O(1) O(V) Small dense graphs
Adjacency List O(V+E) O(deg(v)) O(deg(v)) Most problems
Edge List O(E) O(E) O(E) Kruskal/Bellman-Ford

Default choice in Codeforces:

Use an adjacency list unless there is a specific reason not to.


9. DFS — Depth First Search

DFS explores as far as possible before going back.

Example:

1
├── 2
│   ├── 4
│   └── 5
└── 3

A possible DFS order:

1 → 2 → 4 → 5 → 3

Recursive DFS

void dfs(int u) {
    visited[u] = true;

    for (int v : graph[u]) {
        if (!visited[v]) {
            dfs(v);
        }
    }
}

Complexity

Every vertex is visited once.

Every edge is processed a constant number of times.

Therefore:

$$$ O(V+E) $$$

Space:

$$$ O(V) $$$

for the visited array and recursion stack.


10. BFS — Breadth First Search

BFS explores the graph level by level.

Level 0:       1

Level 1:     2   3

Level 2:    4 5  6

Implementation:

queue<int> q;

q.push(source);
visited[source] = true;

while (!q.empty()) {
    int u = q.front();
    q.pop();

    for (int v : graph[u]) {
        if (!visited[v]) {
            visited[v] = true;
            q.push(v);
        }
    }
}

Complexity:

$$$ O(V+E) $$$

11. DFS vs BFS

Feature DFS BFS
Data structure Stack / recursion Queue
Traversal Deep first Level first
Connected components Yes Yes
Cycle detection Yes Yes
Unweighted shortest path Not naturally Yes
Tree depth problems Excellent Good
Level-order problems Not ideal Excellent

Easy rule

If the problem asks:

"Minimum number of edges?"

Think:

BFS

If the problem asks:

"Explore / components / recursion / structure?"

Think:

DFS


12. BFS for Shortest Path

In an unweighted graph, BFS gives the shortest path in terms of number of edges.

Example:

1 -- 2 -- 4
|         |
3 --------

From 1 to 4:

1 → 2 → 4

Distance:

2

Implementation:

vector<int> dist(n + 1, -1);

queue<int> q;

dist[src] = 0;
q.push(src);

while (!q.empty()) {
    int u = q.front();
    q.pop();

    for (int v : graph[u]) {
        if (dist[v] == -1) {
            dist[v] = dist[u] + 1;
            q.push(v);
        }
    }
}

Complexity:

$$$ O(V+E) $$$

13. Multi-Source BFS

Sometimes there are multiple starting points.

Example:

Sources = {1, 5, 8}

Instead of running BFS three times, put all sources into the queue initially.

for (int src : sources) {
    dist[src] = 0;
    q.push(src);
}

Then perform normal BFS.

Complexity:

$$$ O(V+E) $$$

This is extremely useful in grid problems and shortest-distance-to-nearest-source problems.


14. 0-1 BFS

What if edge weights are only:

0 or 1

Example:

u --0-- v
u --1-- x

Dijkstra works, but 0-1 BFS is faster and simpler.

Use a:

deque<int>

Rules:

  • Weight 0 → push to front
  • Weight 1 → push to back
if (dist[v] > dist[u] + w) {
    dist[v] = dist[u] + w;

    if (w == 0)
        dq.push_front(v);
    else
        dq.push_back(v);
}

Complexity:

$$$ O(V+E) $$$

15. Dijkstra's Algorithm

Dijkstra finds shortest paths from one source when:

All edge weights are non-negative.

Example:

1 --4-- 2
|       |
2       1
|       |
3 --5-- 4

We maintain:

dist[v] = shortest known distance from source to v

The key idea:

Always process the currently closest unprocessed vertex.

With a priority queue:

priority_queue<
    pair<long long,int>,
    vector<pair<long long,int>>,
    greater<pair<long long,int>>
> pq;

Implementation:

vector<long long> dist(n + 1, INF);

dist[src] = 0;
pq.push({0, src});

while (!pq.empty()) {
    auto [d, u] = pq.top();
    pq.pop();

    if (d != dist[u])
        continue;

    for (auto [v, w] : graph[u]) {
        if (dist[v] > d + w) {
            dist[v] = d + w;
            pq.push({dist[v], v});
        }
    }
}

Complexity:

$$$ O((V+E)\log V) $$$

Usually written as:

$$$ O(E\log V) $$$

for connected sparse graphs.


16. Why Dijkstra Does NOT Work With Negative Edges

Suppose:

A → B = 5
A → C = 2
C → B = -10

Dijkstra might finalize B with:

5

before discovering:

A → C → B
= 2 - 10
= -8

Therefore:

Dijkstra requires non-negative edge weights.


17. Bellman-Ford

Bellman-Ford handles:

  • Positive edges
  • Zero edges
  • Negative edges
  • Negative cycle detection

The basic idea is to relax every edge repeatedly.

For every edge:

u → v with weight w

perform:

dist[v] = min(dist[v], dist[u] + w);

Repeat:

V - 1 times

Complexity:

$$$ O(VE) $$$

This is much slower than Dijkstra.


18. Negative Cycle Detection

After performing V - 1 relaxations, perform one more iteration.

If a distance can still be improved:

negative cycle exists

Why?

A shortest simple path can contain at most:

V - 1 edges

If we still improve after that, a cycle must be involved.


19. Floyd-Warshall

What if we need shortest paths between:

Every pair of vertices?

Use Floyd-Warshall.

We maintain:

dist[i][j]

and try every vertex k as an intermediate vertex:

for (int k = 1; k <= n; k++) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            dist[i][j] =
                min(dist[i][j],
                    dist[i][k] + dist[k][j]);
        }
    }
}

Complexity:

$$$ O(V^3) $$$

Space:

$$$ O(V^2) $$$

Best when:

V is small

20. Shortest Path Algorithm Decision

This is one of the most important things to remember.

Graph Algorithm Complexity
Unweighted BFS O(V+E)
Weights are 0/1 0-1 BFS O(V+E)
Non-negative weights Dijkstra O(E log V)
Negative weights Bellman-Ford O(VE)
All-pairs shortest path, small V Floyd-Warshall O(V³)

Quick decision tree

Shortest Path
     |
     ├── Unweighted?
     │      └── BFS
     |
     ├── Weights only 0/1?
     │      └── 0-1 BFS
     |
     ├── Any negative edge?
     │      ├── Yes → Bellman-Ford
     │      └── No  → Dijkstra
     |
     └── All pairs + small V?
            └── Floyd-Warshall

21. DSU — Disjoint Set Union

DSU is also called:

Union-Find

It maintains multiple disjoint sets.

Initially:

1   2   3   4   5

Every node is its own component.

If we perform:

union(1,2)
union(2,3)

we get:

{1,2,3}   {4}   {5}

22. DSU Operations

Two main operations:

Find

Find the representative of a set.

find(x)

Union

Merge two sets.

union(a,b)

With:

  • Path compression
  • Union by size/rank

the amortized complexity is:

$$$ O(\alpha(V)) $$$

where (\alpha) is the inverse Ackermann function.

For practical purposes:

Almost constant time.


23. Kruskal's Algorithm

Kruskal finds a:

Minimum Spanning Tree (MST)

for a connected weighted undirected graph.

The idea:

  1. Sort edges by weight.
  2. Take the smallest edge.
  3. If it doesn't create a cycle, add it.
  4. Otherwise skip it.
  5. Continue until we have V-1 edges.

Example:

Edges:

1 -- 1 -- 2
2 -- 2 -- 3
1 -- 5 -- 3

Choose:

weight 1
weight 2

Total:

3

Kruskal + DSU

This is where DSU becomes extremely useful.

For an edge:

u -- v

if:

find(u) != find(v)

then adding the edge won't create a cycle.

Then:

unite(u, v);

Complexity:

Sorting:

$$$ O(E\log E) $$$

DSU operations:

$$$ O(E\alpha(V)) $$$

Overall:

$$$ O(E\log E) $$$

24. Prim's Algorithm

Prim also finds an MST.

Instead of sorting all edges, it grows the MST from one starting vertex.

With a priority queue:

$$$ O(E\log V) $$$

Both Prim and Kruskal solve the same problem but work differently.


25. Kruskal vs Prim

Feature Kruskal Prim
Main idea Choose smallest edges Grow tree
Main data structure DSU Priority Queue
Complexity O(E log E) O(E log V)
Best for Edge-list problems Adjacency-list problems
Handles disconnected graph Gives minimum spanning forest Needs handling per component

Practical Codeforces rule

If the graph is naturally given as:

list of edges

Kruskal + DSU is often very convenient.


26. Topological Sort

Topological sorting is possible only for:

Directed Acyclic Graphs (DAGs)

It gives an ordering such that:

For every edge:

u → v

u appears before v.

Example:

1 → 3
2 → 3
3 → 4

Valid ordering:

1 2 3 4

or:

2 1 3 4

27. Kahn's Algorithm

Kahn's algorithm uses indegrees.

First calculate:

indegree[v]

Put all nodes with:

indegree = 0

into a queue.

Then:

while (!q.empty()) {
    int u = q.front();
    q.pop();

    order.push_back(u);

    for (int v : graph[u]) {
        indegree[v]--;

        if (indegree[v] == 0)
            q.push(v);
    }
}

If:

order.size() < V

then the graph contains a cycle.

Complexity:

$$$ O(V+E) $$$

28. DFS Topological Sort

Topological sorting can also be done with DFS.

The important idea:

Add a node after processing all its outgoing edges.

Then reverse the resulting list.

Complexity:

$$$ O(V+E) $$$

29. DAG Dynamic Programming

One of the most useful combinations is:

Topological Sort + DP

For example:

Find the longest path in a DAG.

Process nodes in topological order.

For every edge:

u → v

update:

dp[v] = max(dp[v], dp[u] + weight);

Complexity:

$$$ O(V+E) $$$

This is much faster than general shortest/longest path approaches because DAGs have no cycles.


30. Bipartite Graph

A graph is bipartite if we can divide its vertices into two groups such that:

No edge connects vertices inside the same group.

Example:

Group A: 1 3 5

Group B: 2 4 6

Every edge goes:

A ↔ B

31. Checking Bipartite Graph

Use BFS or DFS with two colors.

color[src] = 0;

queue<int> q;
q.push(src);

while (!q.empty()) {
    int u = q.front();
    q.pop();

    for (int v : graph[u]) {
        if (color[v] == -1) {
            color[v] = color[u] ^ 1;
            q.push(v);
        }
        else if (color[v] == color[u]) {
            return false;
        }
    }
}

Complexity:

$$$ O(V+E) $$$

Important fact

An undirected graph is bipartite iff it contains no odd-length cycle.


32. Cycle Detection in Undirected Graph

Using DFS:

bool dfs(int u, int parent) {
    visited[u] = true;

    for (int v : graph[u]) {
        if (!visited[v]) {
            if (dfs(v, u))
                return true;
        }
        else if (v != parent) {
            return true;
        }
    }

    return false;
}

Complexity:

$$$ O(V+E) $$$

33. Cycle Detection in Directed Graph

For directed graphs, simply checking visited[v] isn't enough.

We need to know whether a node is currently in the recursion path.

Use three states:

0 = unvisited
1 = currently visiting
2 = completely processed

If we find an edge:

u → v

where:

state[v] == 1

then we found a cycle.

Complexity:

$$$ O(V+E) $$$

34. Strongly Connected Components

For directed graphs, two vertices belong to the same strongly connected component if:

u → v

and:

v → u

are both possible.

Example:

1 → 2
↑   ↓
4 ← 3

All four nodes can reach each other.

Therefore:

{1,2,3,4}

is one SCC.


35. Kosaraju's Algorithm

Kosaraju finds SCCs in:

$$$ O(V+E) $$$

It uses two DFS passes.

Step 1

Run DFS on the original graph and store nodes by finishing time.

Step 2

Reverse every edge.

Step 3

Process vertices in decreasing finishing-time order.

Each DFS in the reversed graph gives one SCC.


36. Tarjan's SCC Algorithm

Tarjan finds SCCs using a single DFS.

It maintains:

discovery time
low-link value
stack

Complexity:

$$$ O(V+E) $$$

Kosaraju is often easier to implement and understand.

Tarjan is more advanced and useful when you want a one-pass SCC algorithm.


37. Condensation Graph

After finding SCCs, we can compress every SCC into a single node.

Example:

SCC A → SCC B → SCC C

The resulting graph is always a:

DAG

This technique is extremely useful.

A common Codeforces pattern is:

Original graph
      ↓
Find SCC
      ↓
Compress SCCs
      ↓
DAG
      ↓
DP / Topological Sort

38. Bridges

A bridge is an edge whose removal increases the number of connected components.

Example:

1 -- 2 -- 3

The edge:

2 -- 3

is a bridge.

But in:

1
|\ 
| \
2--3

none of the triangle edges is a bridge.


39. Finding Bridges

Use DFS with:

tin[u] = discovery time
low[u] = earliest reachable discovery time

For a DFS tree edge:

u → v

if:

$$$ low[v] \gt tin[u] $$$

then:

(u,v) is a bridge

Complexity:

$$$ O(V+E) $$$

40. Articulation Points

An articulation point is a vertex whose removal increases the number of connected components.

Example:

1 -- 2 -- 3
     |
     4

Removing 2 disconnects the graph.

So:

2 = articulation point

Using the same tin and low concepts, articulation points can be found in:

$$$ O(V+E) $$$

41. Bridges vs Articulation Points

Concept Remove Result
Bridge Edge Components increase
Articulation Point Vertex Components increase

Both can be found using DFS + low-link values.


42. Euler Path and Euler Circuit

An Euler path uses every edge exactly once.

An Euler circuit uses every edge exactly once and returns to the starting vertex.

For an undirected graph:

Euler Circuit

Every vertex has even degree.

Euler Path

Exactly two vertices have odd degree.

Special case:

0 odd-degree vertices

also gives an Euler circuit.


43. Hamiltonian vs Euler

Do not confuse these.

Euler

Visit every:

EDGE

exactly once.

Hamiltonian

Visit every:

VERTEX

exactly once.

Euler problems are often manageable.

Hamiltonian problems are generally much harder and often require special constraints or DP/bitmask techniques.


44. Maximum Flow

Some graph problems aren't about paths or connectivity.

They are about:

How much flow can move from source s to sink t?

Examples:

  • Network capacity
  • Matching
  • Assigning resources
  • Scheduling
  • Cutting networks

Common algorithms:

  • Ford-Fulkerson
  • Edmonds-Karp
  • Dinic

45. Dinic's Algorithm

Dinic works in phases:

  1. BFS builds a level graph.
  2. DFS sends blocking flow.

A standard implementation uses:

struct Edge {
    int to;
    long long cap;
    int rev;
};

Complexity for general graphs is commonly given as:

$$$ O(V^2E) $$$

although actual performance is often much better depending on the graph structure.

For Codeforces, Dinic is one of the most important max-flow implementations to know.


46. Matching

Many matching problems can be transformed into flow.

For bipartite matching:

Left side
   |
   ↓
Edges
   |
   ↓
Right side

A standard flow construction is:

Source
  ↓
Left vertices
  ↓
Possible matches
  ↓
Right vertices
  ↓
Sink

Each edge gets capacity 1.

Then:

maximum flow = maximum matching

47. Grid as a Graph

One of the biggest competitive-programming tricks:

A grid is often just a graph in disguise.

Example:

. . #
. . .
# . .

Each cell can be considered a vertex.

Edges connect neighboring cells.

For four-direction movement:

up
down
left
right

Then standard algorithms work:

BFS
DFS
0-1 BFS
Dijkstra
DSU

depending on the problem.


48. Grid BFS

For an unweighted grid:

int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};

Then:

for (int dir = 0; dir < 4; dir++) {
    int nx = x + dx[dir];
    int ny = y + dy[dir];

    // validate cell

    if (!visited[nx][ny]) {
        visited[nx][ny] = true;
        q.push({nx, ny});
    }
}

Complexity:

$$$ O(NM) $$$

for an N × M grid.


49. DSU on Grids

DSU is useful when cells are dynamically connected.

For example:

Add land
Remove land
Connect components
Count islands

Map a cell:

(x, y)

to:

id = x * m + y

Then use normal DSU operations.


50. Binary Lifting on Trees

Trees are also graphs.

When queries ask:

What is the kth ancestor of node u?

or:

Find LCA of u and v.

Binary lifting is very useful.

Precompute:

up[u][j]

where:

up[u][j] = 2^j-th ancestor of u

Then:

$$$ O(V\log V) $$$

preprocessing.

Each query:

$$$ O(\log V) $$$

51. LCA — Lowest Common Ancestor

For two nodes:

        1
       / \
      2   3
     / \
    4   5

LCA of:

4 and 5

is:

2

LCA of:

4 and 3

is:

1

Common approaches:

  • Binary lifting
  • Euler tour + RMQ
  • Heavy-Light Decomposition

52. Tree Diameter

The diameter of a tree is the longest path between any two vertices.

A simple method:

Step 1

Start BFS/DFS from any node.

Find the farthest node:

A

Step 2

Run BFS/DFS from A.

The farthest distance found is the diameter.

Complexity:

$$$ O(V+E) $$$

For a tree:

$$$ E = V-1 $$$

so this is effectively:

$$$ O(V) $$$

53. Heavy-Light Decomposition

Heavy-Light Decomposition, or HLD, is used for advanced tree queries.

It can convert tree path queries into a small number of array range queries.

Usually combined with:

  • Segment Tree
  • Fenwick Tree

Typical problems:

Update a node
Query path sum
Query path maximum
Update an edge
Query path minimum

Typical complexity:

Preprocessing:

$$$ O(V\log V) $$$

Query:

$$$ O(\log^2 V) $$$

when combined with a segment tree.


54. Graph Algorithm Complexity Cheat Sheet

Here is the important comparison.

Algorithm Problem Complexity
DFS Traversal O(V+E)
BFS Traversal O(V+E)
Multi-source BFS Shortest unweighted distance O(V+E)
0-1 BFS Weights 0/1 O(V+E)
Dijkstra Non-negative weights O(E log V)
Bellman-Ford Negative weights O(VE)
Floyd-Warshall All-pairs shortest path O(V³)
DSU Dynamic connectivity O(α(V)) amortized
Kruskal MST O(E log E)
Prim MST O(E log V)
Topological Sort DAG ordering O(V+E)
SCC — Kosaraju Strong connectivity O(V+E)
SCC — Tarjan Strong connectivity O(V+E)
Bridges Critical edges O(V+E)
Articulation Points Critical vertices O(V+E)
Tree Diameter Longest tree path O(V)
LCA + Binary Lifting Ancestor queries O(log V) per query
Dinic Maximum flow O(V²E) general bound
Floyd-Warshall All-pairs O(V³)

55. The Most Important Decision Table

When you see a graph problem, ask these questions in order.

Question 1 — Is it a tree?

If:

Connected
+
V - 1 edges

then it's a tree.

Think about:

DFS
BFS
Tree DP
LCA
Diameter
HLD

Question 2 — Is it unweighted?

If yes and you need shortest distance:

BFS

Question 3 — Are weights only 0 and 1?

Use:

0-1 BFS

Question 4 — Are all weights non-negative?

Use:

Dijkstra

Question 5 — Are there negative weights?

Think:

Bellman-Ford

Question 6 — Do you need all-pairs shortest paths?

If V is small:

Floyd-Warshall

Question 7 — Do you need minimum spanning tree?

Think:

Kruskal + DSU

or:

Prim

Question 8 — Is it a DAG?

Think:

Topological Sort
+
DP

Question 9 — Is the graph directed and connectivity is complicated?

Think:

SCC

Question 10 — Does removing an edge disconnect the graph?

Think:

Bridge

Question 11 — Does removing a vertex disconnect the graph?

Think:

Articulation Point

Question 12 — Is the graph bipartite?

Think:

BFS/DFS + 2-coloring

Question 13 — Are you repeatedly joining components?

Think:

DSU

Question 14 — Is the problem about sending maximum capacity?

Think:

Max Flow

56. One Mental Map for Graph Algorithms

The easiest way to remember graph algorithms is not by memorizing code.

Memorize the problem → algorithm relationship.

                    GRAPH
                      |
          ┌───────────┴───────────┐
          |                       |
       UNDIRECTED              DIRECTED
          |                       |
      ┌───┴────┐             ┌────┴─────┐
      |        |             |          |
   Unweighted Weighted      DAG       General
      |        |             |          |
     BFS      ┌┴────┐       Topo       SCC
              |     |
            0/1   Non-negative
              |     |
           0-1 BFS Dijkstra

And for special problems:

Connectivity
    ↓
DFS / BFS / DSU

MST
    ↓
Kruskal / Prim

Critical edge
    ↓
Bridge

Critical vertex
    ↓
Articulation Point

All pairs shortest path
    ↓
Floyd-Warshall

Negative edges
    ↓
Bellman-Ford

Maximum capacity
    ↓
Max Flow

Tree queries
    ↓
LCA / HLD / Tree DP

57. How to Approach a Codeforces Graph Problem

Don't immediately start coding.

Use this process.

Step 1 — Identify the vertices

Ask:

What represents a node?

It could be:

city
person
cell
state
index
position
string
configuration

Step 2 — Identify the edges

Ask:

When can I move from one state to another?

For example:

city A → city B

or:

cell (x,y) → cell (nx,ny)

Step 3 — Check direction

Ask:

Can I travel both ways?

If yes:

Undirected

Otherwise:

Directed

Step 4 — Check weights

Ask:

Does every move have the same cost?

If yes:

BFS

If not:

Look at the weights.

Step 5 — Check constraints

This is extremely important.

For example:

V ≤ 20

allows completely different techniques from:

V ≤ 2 × 10^5

Never choose an algorithm without checking constraints.


58. Complexity Is Often the Real Answer

Suppose:

V = 2 × 10^5
E = 2 × 10^5

Then:

O(V²)

is impossible.

You probably want:

O(V+E)

or:

O(E log V)

But if:

V = 500

then:

O(V²)

might be completely fine.

So don't ask only:

"Which algorithm is better?"

Ask:

"Which algorithm fits the constraints?"


59. Common Graph Mistakes

Mistake 1 — Using DFS for unweighted shortest path

DFS can find a path, but it doesn't naturally give the shortest path.

Use:

BFS

Mistake 2 — Using Dijkstra with negative edges

Wrong.

Use:

Bellman-Ford

when negative edges genuinely need to be handled.


Mistake 3 — Forgetting disconnected components

If the graph may be disconnected:

for (int i = 1; i <= n; i++) {
    if (!visited[i]) {
        dfs(i);
    }
}

Don't assume starting from node 1 visits everything.


Mistake 4 — Forgetting graph direction

These are completely different:

u → v

and:

u ↔ v

Mistake 5 — Integer overflow

Shortest paths can exceed int.

Prefer:

long long

for distances when constraints require it.


Mistake 6 — Recursion depth

A DFS on a chain:

1 → 2 → 3 → 4 → ... → 200000

may cause stack overflow in some environments.

For very deep graphs, consider:

iterative DFS

or an appropriately managed stack.


60. Graph Patterns Worth Memorizing

If you are preparing for Codeforces, these patterns are especially valuable.

Pattern 1

Unweighted shortest path
→ BFS

Pattern 2

Connected components
→ DFS/BFS

Pattern 3

0/1 edge weights
→ 0-1 BFS

Pattern 4

Positive weighted shortest path
→ Dijkstra

Pattern 5

Negative edges
→ Bellman-Ford

Pattern 6

Minimum spanning tree
→ Kruskal + DSU

Pattern 7

DAG + ordering
→ Topological Sort

Pattern 8

DAG + optimization
→ Topological Sort + DP

Pattern 9

Directed mutual reachability
→ SCC

Pattern 10

Critical edge
→ Bridge

Pattern 11

Critical vertex
→ Articulation Point

Pattern 12

Repeated component merging
→ DSU

Pattern 13

Tree ancestor queries
→ LCA

Pattern 14

Tree path queries
→ HLD

Pattern 15

Capacity / assignment
→ Max Flow

61. Final Graph Algorithm Cheat Sheet

                 GRAPH
                   |
       ┌───────────┴───────────┐
       |                       |
   Shortest Path           Connectivity
       |                       |
   ┌───┼───────┐          ┌────┼─────┐
   |   |       |          |    |     |
 BFS  0-1   Dijkstra     DFS  BFS   DSU
       BFS
       |
 Negative?
       |
 Bellman-Ford

All pairs?
     |
Floyd-Warshall


Minimum Spanning Tree
        |
   ┌────┴────┐
Kruskal    Prim
   |
  DSU


Directed Graph
      |
   ┌──┴─────┐
  DAG    General
   |         |
 Topo       SCC
   |
  DP


Undirected Graph
       |
   ┌───┴─────┐
 Bridge   Articulation
             Point


Tree
  |
  ├── Diameter
  ├── LCA
  ├── Binary Lifting
  ├── Tree DP
  └── HLD


Capacity
   |
 Max Flow
   |
 Dinic

62. Final Takeaway

You do not need to memorize every graph algorithm at once.

First become extremely comfortable with:

1. Graph representation
2. DFS
3. BFS
4. Connected components
5. Cycle detection
6. Bipartite checking
7. Shortest path
8. Dijkstra
9. DSU
10. Kruskal
11. Topological Sort

Then move to:

12. 0-1 BFS
13. Bellman-Ford
14. SCC
15. Bridges
16. Articulation Points
17. Tree Diameter
18. LCA

Finally:

19. HLD
20. Max Flow
21. Advanced graph DP
22. Advanced tree algorithms

The most important skill is not remembering the implementation.

It is being able to look at a problem and say:

"This is actually a graph problem, and this is the graph algorithm that matches its structure and constraints."

Once you develop that skill, a large class of Codeforces problems becomes much easier to recognize and solve.


Quick Revision

BFS
→ Unweighted shortest path

DFS
→ Traversal / components / structure

0-1 BFS
→ Edge weights 0 or 1

Dijkstra
→ Non-negative weights

Bellman-Ford
→ Negative weights

Floyd-Warshall
→ All-pairs shortest path

DSU
→ Component merging

Kruskal
→ Minimum spanning tree

Prim
→ Minimum spanning tree

Topological Sort
→ DAG ordering

SCC
→ Strong connectivity in directed graphs

Bridge
→ Critical edge

Articulation Point
→ Critical vertex

LCA
→ Tree ancestor/path structure

HLD
→ Advanced tree path queries

Dinic
→ Maximum flow

Graph problems become much easier when you stop thinking of them as "graph problems" and start thinking in terms of patterns:

Shortest path?
Connectivity?
Cycle?
MST?
DAG?
SCC?
Critical edge?
Critical vertex?
Tree query?
Flow?

Identify the pattern first.

Then choose the algorithm.

That is the real graph skill in competitive programming.

Теги graph

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
en1 Английский ripc 2026-09-06 11:50:23 36757 Initial revision (published)