Given N towers numbered from 0 to N - 1, and M wires. Each wire is represented by connections[i][0] and connections[i][1], indicating that these two towers are directly connected. Any towers that are connected directly or indirectly form a group.
If you upgrade any tower in a group, then all towers in that group must be upgraded.
You may upgrade at most X towers in total. Find the maximum number of towers that can be upgraded.
Examples:
Input: N = 4, X = 3, connections[][] = [[1, 2], [0, 3]]
Output: 2
Explanation: Either tower 1 and 2 or tower 3 and 4 can be upgraded.
Input: N = 4, X = 3, connections[][] = [[0, 1], [1, 2],[2, 3]]
Output: 0
Explanation: No tower can be upgraded.
[Naive Approach] Building the graph + DFS - O(2^N) Time and O(N + M) Space
The idea is to first find the sizes of all connected components in the graph and then try every possible subset of these components. Each subset represents choosing some components to upgrade, and the total number of required towers is the sum of their sizes. We check all subsets and keep the maximum sum that does not exceed X. This guarantees correctness because all possible combinations are tested. However, the number of subsets grows exponentially with the number of components. Therefore this method becomes very slow when the number of connected components is large.
C++
#include <iostream>
#include <vector>
using namespace std;
// dfs call to count nodes in a connected component
void dfs(int node, vector<int> adj[], vector<int> &vis, int &cnt) {
vis[node] = 1;
cnt++;
for (int nxt : adj[node]) {
// explore neighbors
if (!vis[nxt]) dfs(nxt, adj, vis, cnt);
}
}
int maxTowers(int N, int M, vector<vector<int>> connections, int X) {
// build graph from given edges
vector<int> adj[N];
for (auto &e : connections) {
adj[e[0]].push_back(e[1]);
adj[e[1]].push_back(e[0]);
}
// store sizes of all connected components
vector<int> vis(N, 0), comp;
for (int i = 0; i < N; i++) {
if (!vis[i]) {
int cnt = 0;
// count this component
dfs(i, adj, vis, cnt);
comp.push_back(cnt);
}
}
int C = comp.size();
int ans = 0;
// try all subsets of components
for (int mask = 0; mask < (1 << C); mask++) {
int sum = 0;
// include selected components
for (int i = 0; i < C; i++) {
if (mask & (1 << i))
sum += comp[i];
}
// check if within allowed limit
if (sum <= X) ans = max(ans, sum);
}
return ans;
}
int main() {
int N = 4, M = 2, X = 3;
vector<vector<int>> connections = {
{1, 2},
{0, 3}
};
cout << maxTowers(N, M, connections, X);
return 0;
}
Java
import java.util.ArrayList;
import java.util.List;
class GFG {
// dfs to count component size
static void dfs(int node, List<List<Integer>> adj, boolean[] vis, int[] cnt) {
// mark node visited
vis[node] = true;
// increase component size
cnt[0]++;
// explore all neighbors
for (int nxt : adj.get(node)) {
if (!vis[nxt]) {
dfs(nxt, adj, vis, cnt);
}
}
}
static int maxTowers(int N, int M, int[][] connections, int X) {
// build graph safely (no generic array)
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i < N; i++) {
adj.add(new ArrayList<>());
}
// add edges
for (int[] e : connections) {
adj.get(e[0]).add(e[1]);
adj.get(e[1]).add(e[0]);
}
// visited array
boolean[] vis = new boolean[N];
// list of component sizes
List<Integer> comp = new ArrayList<>();
// find all components
for (int i = 0; i < N; i++) {
if (!vis[i]) {
// counter for this component
int[] cnt = {0};
// run dfs
dfs(i, adj, vis, cnt);
// store size
comp.add(cnt[0]);
}
}
// number of components
int C = comp.size();
// answer
int ans = 0;
// try all subsets
for (int mask = 0; mask < (1 << C); mask++) {
// total towers in this subset
int sum = 0;
// include selected components
for (int i = 0; i < C; i++) {
if ((mask & (1 << i)) != 0) {
sum += comp.get(i);
}
}
// check limit
if (sum <= X) {
ans = Math.max(ans, sum);
}
}
return ans;
}
public static void main(String[] args) {
int N = 4, M = 2, X = 3;
int[][] connections = {
{1, 2},
{0, 3}
};
System.out.println(maxTowers(N, M, connections, X));
}
}
Python
def dfs(node, adj, vis, cnt):
vis[node] = True
cnt[0] += 1 # increase component size
for nxt in adj[node]:
if not vis[nxt]:
dfs(nxt, adj, vis, cnt)
def maxTowers(N, M, connections, X):
# build graph
adj = [[] for _ in range(N)]
for a, b in connections:
adj[a].append(b)
adj[b].append(a)
# find component sizes
vis = [False] * N
comp = []
for i in range(N):
if not vis[i]:
cnt = [0]
dfs(i, adj, vis, cnt)
comp.append(cnt[0])
ans = 0
C = len(comp)
# try all subsets
for mask in range(1 << C):
total = 0
for i in range(C):
if mask & (1 << i):
total += comp[i]
if total <= X:
ans = max(ans, total)
return ans
if __name__ == "__main__":
N, M, X = 4, 2, 3
connections = [[1, 2], [0, 3]]
print(maxTowers(N, M, connections, X))
C#
using System;
using System.Collections.Generic;
class GFG
{
// dfs to count component size
static void dfs(int node, List<int>[] adj, bool[] vis, ref int cnt)
{
vis[node] = true;
cnt++; // increase component size
foreach (int nxt in adj[node])
{
if (!vis[nxt]) dfs(nxt, adj, vis, ref cnt);
}
}
static int maxTowers(int N, int M, List<List<int>> connections, int X)
{
// build graph
List<int>[] adj = new List<int>[N];
for (int i = 0; i < N; i++) adj[i] = new List<int>();
foreach (var e in connections)
{
adj[e[0]].Add(e[1]);
adj[e[1]].Add(e[0]);
}
bool[] vis = new bool[N];
List<int> comp = new List<int>();
// find component sizes
for (int i = 0; i < N; i++)
{
if (!vis[i])
{
int cnt = 0;
dfs(i, adj, vis, ref cnt);
comp.Add(cnt);
}
}
int C = comp.Count;
int ans = 0;
// try all subsets
for (int mask = 0; mask < (1 << C); mask++)
{
int sum = 0;
for (int i = 0; i < C; i++)
{
if ((mask & (1 << i)) != 0)
sum += comp[i];
}
if (sum <= X) ans = Math.Max(ans, sum);
}
return ans;
}
static void Main()
{
int N = 4, M = 2, X = 3;
var connections = new List<List<int>> {
new List<int>{1, 2},
new List<int>{0, 3}
};
Console.WriteLine(maxTowers(N, M, connections, X));
}
}
JavaScript
function dfs(node, adj, vis, cnt) {
vis[node] = true;
cnt.count++; // increase component size
for (let nxt of adj[node]) {
if (!vis[nxt]) dfs(nxt, adj, vis, cnt);
}
}
function maxTowers(N, M, connections, X) {
// build graph
let adj = Array.from({ length: N }, () => []);
for (let [a, b] of connections) {
adj[a].push(b);
adj[b].push(a);
}
let vis = Array(N).fill(false);
let comp = [];
// find component sizes
for (let i = 0; i < N; i++) {
if (!vis[i]) {
let cnt = { count: 0 };
dfs(i, adj, vis, cnt);
comp.push(cnt.count);
}
}
let ans = 0;
let C = comp.length;
// try all subsets
for (let mask = 0; mask < (1 << C); mask++) {
let sum = 0;
for (let i = 0; i < C; i++) {
if (mask & (1 << i))
sum += comp[i];
}
if (sum <= X) ans = Math.max(ans, sum);
}
return ans;
}
// Driver code
let N = 4, M = 2, X = 3;
let connections = [[1, 2], [0, 3]];
console.log(maxTowers(N, M, connections, X));
[Expected approach] Knapsack DP - O(N × X) Time and O(N + M + X) Space
The expected approach starts by grouping towers into connected components using depth-first search. Each component behaves as a single block because upgrading any tower inside it requires upgrading all towers in that group. After computing the size of every component, the problem reduces to selecting some component sizes whose total does not exceed X. This selection can be done efficiently using a knapsack dynamic programming method where each component size acts like an item weight. The dp array records the maximum number of towers that can be upgraded for each limit up to X. The final answer is the best achievable value in dp[X].
C++
#include <iostream>
#include <vector>
using namespace std;
// dfs to count size of one connected component
void dfsOpt(int node, vector<int> adj[], vector<int> &vis, int &cnt) {
vis[node] = 1;
cnt++;
// explore neighbors
for (int nxt : adj[node]) {
if (!vis[nxt])
dfsOpt(nxt, adj, vis, cnt);
}
}
int maxTowers(int N, int M, vector<vector<int>> connections, int X) {
// build adjacency list
vector<int> adj[N];
for (auto &e : connections) {
adj[e[0]].push_back(e[1]);
adj[e[1]].push_back(e[0]);
}
// list of component sizes
vector<int> vis(N, 0), comp;
// find all connected components
for (int i = 0; i < N; i++) {
if (!vis[i]) {
// count nodes in this component
int cnt = 0;
dfsOpt(i, adj, vis, cnt);
// store component size
comp.push_back(cnt);
}
}
// dp[j] = max towers we can upgrade using limit j
vector<int> dp(X + 1, 0);
// knapsack: choose optimal components
for (int sz : comp) {
for (int j = X; j >= sz; j--) {
dp[j] = max(dp[j], dp[j - sz] + sz);
}
}
return dp[X];
}
int main() {
int N = 4, M = 2, X = 3;
vector<vector<int>> connections = {
{1, 2},
{0, 3}
};
// output result
cout << maxTowers(N, M, connections, X);
return 0;
}
Java
import java.util.ArrayList;
import java.util.List;
class GFG {
// dfs to count component size
static void dfsOpt(int node, List<List<Integer>> adj, boolean[] vis, int[] cnt) {
// mark visited
vis[node] = true;
// increase count
cnt[0]++;
// explore neighbors
for (int nxt : adj.get(node)) {
if (!vis[nxt])
dfsOpt(nxt, adj, vis, cnt);
}
}
static int maxTowers(int N, int M, int[][] connections, int X) {
// build graph
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i < N; i++)
adj.add(new ArrayList<>());
// add edges
for (int[] e : connections) {
adj.get(e[0]).add(e[1]);
adj.get(e[1]).add(e[0]);
}
// visited array
boolean[] vis = new boolean[N];
// component sizes
List<Integer> comp = new ArrayList<>();
// find components
for (int i = 0; i < N; i++) {
if (!vis[i]) {
// store count in array
int[] cnt = {0};
// dfs call
dfsOpt(i, adj, vis, cnt);
// save component size
comp.add(cnt[0]);
}
}
// dp array for knapsack
int[] dp = new int[X + 1];
// knapsack transition
for (int sz : comp) {
for (int j = X; j >= sz; j--) {
dp[j] = Math.max(dp[j], dp[j - sz] + sz);
}
}
return dp[X];
}
public static void main(String[] args) {
int N = 4, M = 2, X = 3;
int[][] connections = {
{1, 2},
{0, 3}
};
System.out.println(maxTowers(N, M, connections, X));
}
}
Python
# dfs to count component size
def dfsOpt(node, adj, vis, cnt):
# mark visited
vis[node] = True
# increase component count
cnt[0] += 1
# explore neighbors
for nxt in adj[node]:
if not vis[nxt]:
dfsOpt(nxt, adj, vis, cnt)
def maxTowers(N, M, connections, X):
# build graph
adj = [[] for _ in range(N)]
for a, b in connections:
adj[a].append(b)
adj[b].append(a)
# visited array
vis = [False] * N
# list of component sizes
comp = []
# find components
for i in range(N):
if not vis[i]:
cnt = [0]
dfsOpt(i, adj, vis, cnt)
comp.append(cnt[0])
# dp array
dp = [0] * (X + 1)
# knapsack update
for sz in comp:
for j in range(X, sz - 1, -1):
dp[j] = max(dp[j], dp[j - sz] + sz)
return dp[X]
if __name__ == "__main__":
N, M, X = 4, 2, 3
connections = [[1, 2], [0, 3]]
print(maxTowers(N, M, connections, X))
C#
using System;
using System.Collections.Generic;
class GFG {
// dfs to count component size
static void dfsOpt(int node, List<List<int>> adj, bool[] vis, ref int cnt) {
// mark visited
vis[node] = true;
// increase size
cnt++;
// explore neighbors
foreach (int nxt in adj[node]) {
if (!vis[nxt])
dfsOpt(nxt, adj, vis, ref cnt);
}
}
static int maxTowers(int N, int M, List<List<int>> connections, int X) {
// build graph
List<List<int>> adj = new List<List<int>>();
for (int i = 0; i < N; i++)
adj.Add(new List<int>());
// add edges
foreach (var e in connections) {
adj[e[0]].Add(e[1]);
adj[e[1]].Add(e[0]);
}
// visited array
bool[] vis = new bool[N];
// component list
List<int> comp = new List<int>();
// find components
for (int i = 0; i < N; i++) {
if (!vis[i]) {
int cnt = 0;
dfsOpt(i, adj, vis, ref cnt);
comp.Add(cnt);
}
}
// dp array
int[] dp = new int[X + 1];
// knapsack
foreach (int sz in comp) {
for (int j = X; j >= sz; j--) {
dp[j] = Math.Max(dp[j], dp[j - sz] + sz);
}
}
return dp[X];
}
static void Main() {
int N = 4, M = 2, X = 3;
var connections = new List<List<int>> {
new List<int> {1, 2},
new List<int> {0, 3}
};
Console.WriteLine(maxTowers(N, M, connections, X));
}
}
JavaScript
// dfs to count component size
function dfsOpt(node, adj, vis, cnt) {
// mark visited
vis[node] = true;
// increase count
cnt.count++;
// explore neighbors
for (let nxt of adj[node]) {
if (!vis[nxt])
dfsOpt(nxt, adj, vis, cnt);
}
}
function maxTowers(N, M, connections, X) {
// build graph
let adj = Array.from({ length: N }, () => []);
// add edges
for (let [a, b] of connections) {
adj[a].push(b);
adj[b].push(a);
}
// visited array
let vis = Array(N).fill(false);
// list of component sizes
let comp = [];
// find components
for (let i = 0; i < N; i++) {
if (!vis[i]) {
// store count in object
let cnt = { count: 0 };
// dfs call
dfsOpt(i, adj, vis, cnt);
// save component size
comp.push(cnt.count);
}
}
// dp array
let dp = Array(X + 1).fill(0);
// knapsack DP
for (let sz of comp) {
for (let j = X; j >= sz; j--) {
dp[j] = Math.max(dp[j], dp[j - sz] + sz);
}
}
return dp[X];
}
// driver code
let N = 4, M = 2, X = 3;
let connections = [[1, 2], [0, 3]];
console.log(maxTowers(N, M, connections, X));
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem