-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSolution.cpp
159 lines (140 loc) · 4.54 KB
/
Solution.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <queue>
#include <vector>
class UnionFind {
public:
UnionFind(int n) : parent_(n), rank_(n, 0) {
for (int i = 0; i < n; ++i) {
parent_[i] = i;
}
}
int find(int x) {
if (parent_[x] != x) {
parent_[x] = find(parent_[x]);
}
return parent_[x];
}
bool connected(int x, int y) { return find(x) == find(y); }
void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) {
return;
}
if (rank_[rootX] < rank_[rootY]) {
parent_[rootX] = rootY;
return;
}
if (rank_[rootX] == rank_[rootY]) {
++rank_[rootX];
}
parent_[rootY] = rootX;
}
private:
std::vector<int> parent_;
std::vector<int> rank_;
};
class Solution {
public:
int magnificentSets(int n, std::vector<std::vector<int>>& edges) {
// Given edges, where edges[i] = [a, b] indicate a bidirectional edge
// between nodes a and b.
//
// To divide the nodes into groups such that:
// - Each node belongs to exactly one group,
// - Every pair of nodes connected by an edge must belong to adjacent
// groups, i.e., if a in group X, and b in group Y, then |X - Y| = 1.
// To maximize the number of groups. -1 if not possible.
//
// First glance seems like Topological Sort. But edges are bidirectional.
// Very similar to Graph Coloring too, i.e., Bipartite Graphs.
// i.e., no two nodes connected by an edge have the same color. Therefore,
// only two colours are needed.
//
// Suppose we root the graph at an arbitrary node, say 1.
// Then, start with an arbitrary colour, say Red->Blue. Assign Red, to 1,
// Blue to all its neighbour, and DFS.
// If any edges can not be colored different, then it is not possible for
// Groups to be assigned.
// At the end, we will have two groups of nodes.
// How to maximize the number of groups, while still fulfilling the
// constraints then?
// How can we detect nodes in the same group, that do not need to be
// grouped together?
//
// Alternatively, what if we brute-force with each node as the root of the
// Graph? i.e., assign groups corresponding to the node's depth. Seems
// plausible.
//
// WARNING: input graph may not be a single connected component.
// Thus, there is a need to find the max group of each component.
// UF can be used to identify which nodes belong to which component.
UnionFind G(n);
std::vector<std::vector<int>> adj(n);
for (const auto& edge : edges) {
// convert to 0-index.
adj[edge[0] - 1].push_back(edge[1] - 1);
adj[edge[1] - 1].push_back(edge[0] - 1);
G.unite(edge[0] - 1, edge[1] - 1);
}
// return -1 if the depths are not consecutive.
auto bfs = [n, &adj](int src) -> int {
// distance from the root.
std::vector<int> color(n, -1);
std::queue<int> frontier{{src}};
color[src] = 0;
// Need to ensure nodes connected by an edge have
// |depth[u] - depth[v]| = 1
// Checks need to be done when "revisiting" nodes.
int currColor = 1;
int distance = 0;
while (!frontier.empty()) {
int sz = frontier.size();
while (sz--) {
int curr = frontier.front();
frontier.pop();
for (int next : adj[curr]) {
if (color[next] == -1) {
color[next] = currColor;
frontier.push(next);
} else if (color[next] == color[curr]) {
return -1; // constraints are violated.
}
}
}
currColor = 1 - currColor;
++distance;
}
// No need to +1 to include the root group, should be done in the last
// iteration.
return distance;
};
std::vector<bool> processed(n, false);
std::vector<int> componentNumGroups(n, 0);
int result = 0;
for (int node = 0; node < n; ++node) {
int component = G.find(node);
if (processed[component]) {
continue;
}
processed[component] = true;
// try each node in THIS component as the root.
for (int root = 0; root < n; ++root) {
if (!G.connected(component, root)) {
continue;
}
int depth = bfs(root);
if (depth == -1) {
// constraints cannot be fulfilled.
return -1;
}
componentNumGroups[component] =
std::max(componentNumGroups[component], depth);
}
result += componentNumGroups[component];
}
return result;
}
};