I wanted to solve this problem: GCD on directed graph
I'm new to SCC, topological ordering, Kosajaru algorithm etc.
Generally, I think that the more nodes we use in path the better result can be, because the cost (GCD) will never grow. This is my approach to this problem:
- Find all strongly connected components (so I use as many nodes as possible) and the GCD (cost of traversing whole component)
- We can map found SCCs to nodes and make DAG where single node represents whole SCC
This way we reduced the problem to finding the shortest path (from any to any node) in weighted DGA. But I have no idea how can I approach it with acceptable time complexity. That's how my current code looks:
#include <iostream>
#include <vector>
#include <list>
#include <stack>
using namespace std;
int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
void top_sort(const vector<list<int>>& g, vector<bool>& visited, vector<int>& order, int node) {
visited[node] = true;
for (const int ngbr : g[node]) {
if (visited[ngbr])
continue;
top_sort(g, visited, order, ngbr);
}
order.push_back(node);
}
void dfs_0(const vector<list<int>>& rg, const vector<int>& c, vector<bool>& visited, vector<int>& components_gcd, vector<int>& components, int node, int component) {
visited[node] = true;
components_gcd[component] = gcd(components_gcd[component], c[node]);
components[node] = component;
for (const int ngbr : rg[node]) {
if (visited[ngbr])
continue;
dfs_0(rg, c, visited, components_gcd, components, ngbr, component);
}
}
int solve(int n, vector<int> c, vector<vector<int>> edges) {
vector<list<int>> g(n + 1, list<int>());
vector<list<int>> rg(n + 1, list<int>());
for (int i = 0; i < edges.size(); ++i) {
g[edges[i][0]].push_back(edges[i][1]);
rg[edges[i][1]].push_back(edges[i][0]);
}
vector<bool> visited(n + 1);
vector<int> order;
order.reserve(n);
for (int i = 1; i <= n; ++i) {
if (!visited[i])
top_sort(g, visited, order, i);
}
reverse(order.begin(), order.end());
fill(visited.begin(), visited.end(), false);
vector<int> components_gcd(n + 1);
vector<int> components(n + 1);
int component = 0;
for (const int node : order) {
if (!visited[node]) {
++component;
dfs_0(rg, c, visited, components_gcd, components, node, component);
}
}
vector<list<int>> scc_g(component, list<int>());
for (int i = 0; i < edges.size(); ++i) {
if (components[edges[i][0]] != components[edges[i][1]]) {
scc_g[components[edges[i][0]]].push_back(components[edges[i][1]]);
}
}
// I struggle here. scc_g is our DAG with SCCs as single nodes
}
Please comment if something is unclear. Thanks
Given that:
GCD(a, b) ≤ aGCD(a, b) ≤ bThen you know that for a graph G(V,E) where the vertex vn has a cost cn then for every edge e(i,j) = (vi, vj) the cost c(i,j) for the edge is GCD(ci, cj) which is always less than or equal to both ci and cj.
Additionally, GCDs are:
GCD(a, b) = GCD(b, a)GCD(GCD(a, b), c) = GCD(a, GCD(b, c))So if you have any graph G(V, E) then:
Find the strongly connected components in the graph and:
For each vertex with no inbound edges, generate a set of maximal walks starting from that vertex by recursively following all the outbound edges (if they exist) from that vertex to find the maximal walks can calculate the GCD of the cost of all vertices on a path.
Note: having removed the SCCs there should be no cycles on those paths but you may have to visit vertices multiple times on different paths and branching paths that later converge may have different GCDs. I.e. (12->8->24) and (12->9->24) that share the same start and end have GCDs of 4 and 3 respectively. This means that the algorithm should be O(E) bounded rather than O(V).
The GCD for the graph is the minimum of the GCDs in each of those maximal walks.
In python, I think you could implement it using: