Dijkstra algorithm implementation with adjacency list
Advice 1

int n = adj.size();


I would change int to size_t, since it counts size that may not be negative.

Advice 2

for (int j = 0; j &lt; n - 1; ++j) {
    ...
}


I would change it to 

while (!candidates.empty()) {

}


This omits some unnecessary computation in case the graph is disconnected.

Advice 3

candidates may be renamed to open, open_list or frontier; that is more or less conventional naming for that data structure in pathfinding community.

Advice 4

I would change typedef std::vector&lt;std::vector&lt;int&gt;&gt; Weights; to typedef std::unordered_map&lt;int, std::unordered_map&lt;int, size_t&gt;&gt; Weights;. This makes filling the edge weights much more easier; especially on sparse graphs:

w[a][b1] = 3;
w[a][b2] = 1;
...


Advice 5

if (!visited[neighbor] &amp;&amp; (dist[next] + weights[next][neighbor] &lt; dist[neighbor])) {
    ...
}


This condition is a bug. It should be 

if (dist[neighbor] == INT_MAX || (dist[next] + weights[next][neighbor] &lt; dist[neighbor])) {
    ...
}


(Note dist[neighbor] == INT_MAX and || instead of &amp;&amp;. You can try changing || to &amp;&amp; and see its effect here.)