Open In App

Minimize the Maximum value

Last Updated : 07 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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: 4

Input: 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:

C++




#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;
}


Java




/*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);
    }
}


Python3




# 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)


C#




//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);
    }
}


Javascript




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);


Output

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).



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads