Given a directed graph of n nodes (numbered from 1 to n) without any cycles. Each vertex of the graph is assigned a value. You have to choose a path of exactly k length such that the maximum value encountered in the path is as small as possible.
Example:
Input: n = 6, k = 4, weight = {1, 10, 2, 3, 4, 5}, edges[][2] = { { 1, 2 }, { 1, 3 }, { 3, 4 }, { 4, 5 }, { 5, 6 }, { 6, 2 }, { 2, 5 } }
Output: 4Input: n = 2, k = 5, weight = {1, 1}, edges[][2] = { { 1, 2 } }
Output: -1
Approach:
The existence of the answer in relation to the minimum value of the maximum in the path is monotonous. Constructing a path with a maximum not greater than x implies the ability to construct a path with a maximum not greater than x + 1. This suggests the use of binary search to find the answer.
In a binary search approach, we aim to find an integer x such that there exists a path in the graph consisting of k−1 edges, and the maximum value on this path is not greater than x. Initially, we focus on vertices with values less than or equal to x. We then check if the required path exists in the resulting graph.
If the graph contains a cycle, it implies a path of every length exists, including k−1. Otherwise, if the graph is a directed acyclic graph, we find the longest path and compare its length with k−1. Sorting the graph topologically, we compute dp[v] – the length of the longest path starting from vertex v, a well-known classical problem.
Step-by-step approach:
-
DFS for Cycle Detection and Longest Path Calculation:
- The dfs function performs a depth-first search to detect cycles and calculate the length of the longest path in the subtree rooted at a particular node.
- It returns true if a cycle is detected during the DFS, otherwise false.
-
Path Existence Check:
- The isPathPossible function checks if there exists a path of length k with the maximum value not exceeding x.
- It uses the dfs function for cycle detection and to calculate the length of the longest path.
-
Binary Search for Maximum Value:
- Binary search is performed on the range of possible maximum values (left to right).
- The isPathPossible function is called within the binary search to determine if a valid path exists for a given mid value.
Below is the implementation of the above approach:
#include <bits/stdc++.h> using namespace std;
#define FOR(i, n) for (int i = 0; i < n; i++) #define FOR1(i, n) for (int i = 1; i <= n; i++) #define ll long long #define vt vector #define pb push_back vt< int > adjList[200005]; // Adjacency list to represent the
// graph
int n, m, weight[200005], a, b, visited[200005],
currentPath[200005];
ll k; // Function to perform depth-first search to detect cycles // and calculate the length of the longest path bool dfs( int node, int x)
{ // If the node is already visited, return false
if (visited[node])
return false ;
// Mark the current node as part of the current path
currentPath[node] = 1;
// Variable to store the length of the longest path in
// the subtree rooted at 'node'
int result = 0;
// Iterate through neighbors of the current node
for ( auto neighbor : adjList[node]) {
// If the weight of the neighbor is less than or
// equal to 'x'
if (weight[neighbor] <= x) {
// If the neighbor is already part of the
// current path, a cycle is detected
if (currentPath[neighbor])
return true ;
// Recursively perform DFS for the neighbor
if (dfs(neighbor, x))
return true ;
// Update the result with the longest path in
// the subtree rooted at the neighbor
result = max(result, visited[neighbor]);
}
}
// Reset the current node in the current path
currentPath[node] = 0;
// Set the length of the longest path in the subtree
// rooted at 'node'
visited[node] = result + 1;
// Return false as there is no cycle detected in the
// subtree rooted at 'node'
return false ;
} // Function to check if a path of length k exists with a // maximum value not exceeding x bool isPathPossible( int x)
{ FOR1(i, n)
{
if (!visited[i] && weight[i] <= x) {
if (dfs(i, x))
return true ;
if (visited[i] >= k)
return true ;
}
}
return false ;
} int main()
{ n = 6, m = 7, k = 4; // Static input
int weights[] = { 1, 10, 2, 3, 4, 5 };
FOR1(i, n)
weight[i] = weights[i - 1]; // Assigning weights
int edges[][2]
= { { 1, 2 }, { 1, 3 }, { 3, 4 }, { 4, 5 },
{ 5, 6 }, { 6, 2 }, { 2, 5 } };
FOR(i, m)
{
a = edges[i][0], b = edges[i][1];
adjList[a].pb(b); // Constructing the directed graph
}
int left = 0, right = 1e9, mid, answer = -1;
while (left <= right) {
mid = left + (right - left) / 2;
FOR1(i, n)
visited[i] = currentPath[i]
= 0; // Resetting arrays for a new iteration
if (isPathPossible(mid)) {
right = mid - 1;
answer = mid;
}
else {
left = mid + 1;
}
}
// Output the result
cout << answer << endl;
return 0;
} |
/*code by flutterfly */ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
public class Main {
static Vector<Integer>[] adjList = new Vector[ 200005 ]; // Adjacency list to represent
// the graph
static int n, m, weight[] = new int [ 200005 ], a, b,
visited[] = new int [ 200005 ], currentPath[] = new int [ 200005 ];
static long k;
// Function to perform depth-first search to detect cycles
// and calculate the length of the longest path
static boolean dfs( int node, int x) {
// If the node is already visited, return false
if (visited[node] != 0 )
return false ;
// Mark the current node as part of the current path
currentPath[node] = 1 ;
// Variable to store the length of the longest path in
// the subtree rooted at 'node'
int result = 0 ;
// Iterate through neighbors of the current node
for ( int neighbor : adjList[node]) {
// If the weight of the neighbor is less than or
// equal to 'x'
if (weight[neighbor] <= x) {
// If the neighbor is already part of the
// current path, a cycle is detected
if (currentPath[neighbor] != 0 )
return true ;
// Recursively perform DFS for the neighbor
if (dfs(neighbor, x))
return true ;
// Update the result with the longest path in
// the subtree rooted at the neighbor
result = Math.max(result, visited[neighbor]);
}
}
// Reset the current node in the current path
currentPath[node] = 0 ;
// Set the length of the longest path in the subtree
// rooted at 'node'
visited[node] = result + 1 ;
// Return false as there is no cycle detected in the
// subtree rooted at 'node'
return false ;
}
// Function to check if a path of length k exists with a
// maximum value not exceeding x
static boolean isPathPossible( int x) {
for ( int i = 1 ; i <= n; i++) {
if (visited[i] == 0 && weight[i] <= x) {
if (dfs(i, x))
return true ;
if (visited[i] >= k)
return true ;
}
}
return false ;
}
public static void main(String[] args) {
n = 6 ;
m = 7 ;
k = 4 ; // Static input
int weights[] = { 1 , 10 , 2 , 3 , 4 , 5 };
for ( int i = 1 ; i <= n; i++)
weight[i] = weights[i - 1 ]; // Assigning weights
int edges[][] = {{ 1 , 2 }, { 1 , 3 }, { 3 , 4 }, { 4 , 5 }, { 5 , 6 }, { 6 , 2 }, { 2 , 5 }};
for ( int i = 0 ; i < 200005 ; i++) {
adjList[i] = new Vector<>();
}
for ( int i = 0 ; i < m; i++) {
a = edges[i][ 0 ];
b = edges[i][ 1 ];
adjList[a].add(b); // Constructing the directed graph
}
int left = 0 , right = ( int ) 1e9, mid, answer = - 1 ;
while (left <= right) {
mid = left + (right - left) / 2 ;
for ( int i = 1 ; i <= n; i++) {
visited[i] = currentPath[i] = 0 ; // Resetting arrays for a new iteration
}
if (isPathPossible(mid)) {
right = mid - 1 ;
answer = mid;
} else {
left = mid + 1 ;
}
}
// Output the result
System.out.println(answer);
}
} |
# code by flutterfly from typing import List , Tuple , Union
adjList = [[] for _ in range ( 200005 )] # Adjacency list to represent the graph
n, m, k = 6 , 7 , 4 # Static input
weights = [ 1 , 10 , 2 , 3 , 4 , 5 ]
weight = [ 0 ] + weights # Assigning weights
edges = [[ 1 , 2 ], [ 1 , 3 ], [ 3 , 4 ], [ 4 , 5 ], [ 5 , 6 ], [ 6 , 2 ], [ 2 , 5 ]]
for i in range (m):
a, b = edges[i][ 0 ], edges[i][ 1 ]
adjList[a].append(b) # Constructing the directed graph
visited = [ 0 ] * (n + 1 )
currentPath = [ 0 ] * (n + 1 )
# Function to perform depth-first search to detect cycles # and calculate the length of the longest path def dfs(node: int , x: int ) - > Union[ bool , int ]:
# If the node is already visited, return False
if visited[node]:
return False
# Mark the current node as part of the current path
currentPath[node] = 1
# Variable to store the length of the longest path in
# the subtree rooted at 'node'
result = 0
# Iterate through neighbors of the current node
for neighbor in adjList[node]:
# If the weight of the neighbor is less than or
# equal to 'x'
if weight[neighbor] < = x:
# If the neighbor is already part of the
# current path, a cycle is detected
if currentPath[neighbor]:
return True
# Recursively perform DFS for the neighbor
if dfs(neighbor, x):
return True
# Update the result with the longest path in
# the subtree rooted at the neighbor
result = max (result, visited[neighbor])
# Reset the current node in the current path
currentPath[node] = 0
# Set the length of the longest path in the subtree
# rooted at 'node'
visited[node] = result + 1
# Return False as there is no cycle detected in the
# subtree rooted at 'node'
return False
# Function to check if a path of length k exists with a # maximum value not exceeding x def is_path_possible(x: int ) - > bool :
for i in range ( 1 , n + 1 ):
if not visited[i] and weight[i] < = x:
if dfs(i, x):
return True
if visited[i] > = k:
return True
return False
left, right, answer = 0 , 1e9 , - 1
while left < = right:
mid = left + (right - left) / / 2
visited = [ 0 ] * (n + 1 )
currentPath = [ 0 ] * (n + 1 ) # Resetting arrays for a new iteration
if is_path_possible(mid):
right = mid - 1
answer = mid
else :
left = mid + 1
# Output the result print (answer)
|
//code by flutterfly using System;
using System.Collections.Generic;
class Program
{ static List< int >[] adjList;
static int n, m;
static int [] weight, visited, currentPath;
static long k;
// Function to perform depth-first search to detect cycles
// and calculate the length of the longest path
static bool dfs( int node, int x)
{
// If the node is already visited, return false
if (visited[node] != 0)
return false ;
// Mark the current node as part of the current path
currentPath[node] = 1;
// Variable to store the length of the longest path in
// the subtree rooted at 'node'
int result = 0;
// Iterate through neighbors of the current node
foreach ( var neighbor in adjList[node])
{
// If the weight of the neighbor is less than or
// equal to 'x'
if (weight[neighbor] <= x)
{
// If the neighbor is already part of the
// current path, a cycle is detected
if (currentPath[neighbor] != 0)
return true ;
// Recursively perform DFS for the neighbor
if (dfs(neighbor, x))
return true ;
// Update the result with the longest path in
// the subtree rooted at the neighbor
result = Math.Max(result, visited[neighbor]);
}
}
// Reset the current node in the current path
currentPath[node] = 0;
// Set the length of the longest path in the subtree
// rooted at 'node'
visited[node] = result + 1;
// Return false as there is no cycle detected in the
// subtree rooted at 'node'
return false ;
}
// Function to check if a path of length k exists with a
// maximum value not exceeding x
static bool isPathPossible( int x)
{
for ( int i = 1; i <= n; i++)
{
if (visited[i] == 0 && weight[i] <= x)
{
if (dfs(i, x))
return true ;
if (visited[i] >= k)
return true ;
}
}
return false ;
}
static void Main()
{
n = 6;
m = 7;
k = 4;
int [] weights = { 1, 10, 2, 3, 4, 5 };
weight = new int [n + 1];
Array.Copy(weights, 0, weight, 1, n);
adjList = new List< int >[n + 1];
for ( int i = 1; i <= n; i++)
adjList[i] = new List< int >();
int [,] edges = { { 1, 2 }, { 1, 3 }, { 3, 4 }, { 4, 5 }, { 5, 6 }, { 6, 2 }, { 2, 5 } };
for ( int i = 0; i < m; i++)
{
int a = edges[i, 0];
int b = edges[i, 1];
adjList[a].Add(b);
}
int left = 0, right = ( int )1e9, mid, answer = -1;
visited = new int [n + 1];
currentPath = new int [n + 1];
while (left <= right)
{
mid = left + (right - left) / 2;
for ( int i = 1; i <= n; i++)
visited[i] = currentPath[i] = 0;
if (isPathPossible(mid))
{
right = mid - 1;
answer = mid;
}
else
{
left = mid + 1;
}
}
// Output the result
Console.WriteLine(answer);
}
} |
class Graph { constructor() {
this .adjList = [];
this .visited = [];
this .currentPath = [];
this .weight = [];
}
addEdge(a, b) {
// Function to add an edge to the adjacency list
this .adjList[a].push(b);
}
dfs(node, x) {
// Function to perform depth-first search to detect cycles
// and calculate the length of the longest path
if ( this .visited[node] !== 0) {
// If the node is already visited, return false
return false ;
}
this .currentPath[node] = 1;
let result = 0;
for (const neighbor of this .adjList[node]) {
// Iterate through neighbors of the current node
if ( this .weight[neighbor] <= x) {
// If the weight of the neighbor is less than or
// equal to 'x'
if ( this .currentPath[neighbor] !== 0) {
// If the neighbor is already part of the
// current path, a cycle is detected
return true ;
}
if ( this .dfs(neighbor, x)) {
// Recursively perform DFS for the neighbor
return true ;
}
result = Math.max(result, this .visited[neighbor]);
}
}
this .currentPath[node] = 0;
this .visited[node] = result + 1;
return false ;
}
isPathPossible(x) {
// Function to check if a path of length k exists with a
// maximum value not exceeding x
for (let i = 1; i <= this .n; i++) {
if ( this .visited[i] === 0 && this .weight[i] <= x) {
if ( this .dfs(i, x)) {
return true ;
}
if ( this .visited[i] >= this .k) {
return true ;
}
}
}
return false ;
}
findLongestPath(n, m, k, weights, edges) {
// Main function to find the longest path
this .n = n;
this .m = m;
this .k = k;
this .weight = Array.from({ length: n + 1 }, (_, i) => (i === 0 ? 0 : weights[i - 1]));
this .adjList = Array.from({ length: n + 1 }, () => []);
for (let i = 0; i < m; i++) {
const a = edges[i][0];
const b = edges[i][1];
this .addEdge(a, b);
}
let left = 0;
let right = 1e9;
let answer = -1;
this .visited = Array.from({ length: n + 1 }, () => 0);
this .currentPath = Array.from({ length: n + 1 }, () => 0);
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
for (let i = 1; i <= n; i++) {
this .visited[i] = this .currentPath[i] = 0;
}
if ( this .isPathPossible(mid)) {
right = mid - 1;
answer = mid;
} else {
left = mid + 1;
}
}
// Output the result
console.log(answer);
}
} const graph = new Graph();
const n = 6; const m = 7; const k = 4; const weights = [1, 10, 2, 3, 4, 5]; const edges = [ [1, 2],
[1, 3],
[3, 4],
[4, 5],
[5, 6],
[6, 2],
[2, 5],
]; graph.findLongestPath(n, m, k, weights, edges); |
4
Time COmplexity: O(log2(range) * (V + E)), where V is the number of vertices and E is the number of edges.
Auxiliary space: O(V).