Given a highway of length m miles and n possible billboard locations represented by the sorted array x[], where placing a billboard at position x[i] earns revenue[i], find the maximum revenue that can be generated.
The distance between any two selected billboards must be greater than t miles. Therefore, no two billboards can be placed within t miles (or less) of each other.
Note: All billboard positions satisfy 0 âĪ x[i] âĪ m.
Examples:Â
Input: m = 20,
x[] = [6, 7, 12, 13, 14],
revenue[] = [5, 6, 5, 3, 1],
t = 5
Output: 10
Explanation: Place billboards at positions 6 and 12. Total revenue = 5 + 5 = 10.Input: m = 15,
x[] = [6, 9, 12, 14],
revenue[] = [5, 6, 3, 7],
t = 2
Output: 18
Explanation: Place billboards at positions 6, 9, and 14. Total revenue = 5 + 6 + 7 = 18.
Table of Content
[Naive Approach] Using Recursion - O(2 ^ n) Time and O(n) Space
The idea is to recursively decide for every billboard whether to place it or skip it.
- If we place the current billboard, then all billboards within t miles must be skipped.
- Otherwise, simply move to the next billboard.
Finally, return the maximum of both choices.
Working of Approach:
- Start from the first billboard.
- Recursively calculate revenue by skipping the current billboard.
- Recursively calculate revenue by taking the current billboard and moving to the next valid billboard.
- Return the maximum of both values.
- The recursion explores all possible valid billboard selections.
#include <iostream>
#include <vector>
using namespace std;
// Returns maximum revenue starting from the current billboard.
int maxRevenueFromIndex(vector<int> &x, vector<int> &revenue, int n, int t, int index)
{
// Base Case
if (index >= n)
return 0;
// Skip the current billboard.
int skip = maxRevenueFromIndex(x, revenue, n, t, index + 1);
// Find the next valid billboard.
int nextIndex = index + 1;
while (nextIndex < n && x[nextIndex] - x[index] <= t)
nextIndex++;
// Place the current billboard.
int take = revenue[index] + maxRevenueFromIndex(x, revenue, n, t, nextIndex);
return max(skip, take);
}
int maxRevenue(int m, vector<int> &x, vector<int> &revenue, int t)
{
return maxRevenueFromIndex(x, revenue, x.size(), t, 0);
}
int main()
{
int m = 20;
vector<int> x = {6, 7, 12, 13, 14};
vector<int> revenue = {5, 6, 5, 3, 1};
int t = 5;
cout << maxRevenue(m, x, revenue, t);
return 0;
}
import java.util.Arrays;
public class GFG {
// Returns maximum revenue starting from the current
// billboard.
public static int maxRevenueFromIndex(int[] x,
int[] revenue,
int n, int t,
int index)
{
// Base Case
if (index >= n)
return 0;
// Skip the current billboard.
int skip = maxRevenueFromIndex(x, revenue, n, t,
index + 1);
// Find the next valid billboard.
int nextIndex = index + 1;
while (nextIndex < n
&& x[nextIndex] - x[index] <= t)
nextIndex++;
// Place the current billboard.
int take = revenue[index]
+ maxRevenueFromIndex(x, revenue, n, t,
nextIndex);
return Math.max(skip, take);
}
public static int maxRevenue(int m, int[] x,
int[] revenue, int t)
{
return maxRevenueFromIndex(x, revenue, x.length, t,
0);
}
public static void main(String[] args)
{
int m = 20;
int[] x = { 6, 7, 12, 13, 14 };
int[] revenue = { 5, 6, 5, 3, 1 };
int t = 5;
System.out.println(maxRevenue(m, x, revenue, t));
}
}
def maxRevenueFromIndex(x, revenue, n, t, index):
# Base Case
if index >= n:
return 0
# Skip the current billboard.
skip = maxRevenueFromIndex(x, revenue, n, t, index + 1)
# Find the next valid billboard.
nextIndex = index + 1
while nextIndex < n and x[nextIndex] - x[index] <= t:
nextIndex += 1
# Place the current billboard.
take = revenue[index] + maxRevenueFromIndex(x, revenue, n, t, nextIndex)
return max(skip, take)
def maxRevenue(m, x, revenue, t):
return maxRevenueFromIndex(x, revenue, len(x), t, 0)
if __name__ == '__main__':
m = 20
x = [6, 7, 12, 13, 14]
revenue = [5, 6, 5, 3, 1]
t = 5
print(maxRevenue(m, x, revenue, t))
using System;
public class GFG {
// Returns maximum revenue starting from the current
// billboard.
public static int MaxRevenueFromIndex(int[] x,
int[] revenue,
int n, int t,
int index)
{
// Base Case
if (index >= n)
return 0;
// Skip the current billboard.
int skip = MaxRevenueFromIndex(x, revenue, n, t,
index + 1);
// Find the next valid billboard.
int nextIndex = index + 1;
while (nextIndex < n
&& x[nextIndex] - x[index] <= t)
nextIndex++;
// Place the current billboard.
int take = revenue[index]
+ MaxRevenueFromIndex(x, revenue, n, t,
nextIndex);
return Math.Max(skip, take);
}
public static int MaxRevenue(int m, int[] x,
int[] revenue, int t)
{
return MaxRevenueFromIndex(x, revenue, x.Length, t,
0);
}
public static void Main()
{
int m = 20;
int[] x = { 6, 7, 12, 13, 14 };
int[] revenue = { 5, 6, 5, 3, 1 };
int t = 5;
Console.WriteLine(MaxRevenue(m, x, revenue, t));
}
}
function maxRevenueFromIndex(x, revenue, n, t, index)
{
// Base Case
if (index >= n)
return 0;
// Skip the current billboard.
let skip
= maxRevenueFromIndex(x, revenue, n, t, index + 1);
// Find the next valid billboard.
let nextIndex = index + 1;
while (nextIndex < n && x[nextIndex] - x[index] <= t)
nextIndex++;
// Place the current billboard.
let take = revenue[index]
+ maxRevenueFromIndex(x, revenue, n, t,
nextIndex);
return Math.max(skip, take);
}
function maxRevenue(m, x, revenue, t)
{
return maxRevenueFromIndex(x, revenue, x.length, t, 0);
}
// Driver Code
let m = 20;
let x = [ 6, 7, 12, 13, 14 ];
let revenue = [ 5, 6, 5, 3, 1 ];
let t = 5;
console.log(maxRevenue(m, x, revenue, t));
Output
10
[Better Approach] Using Bottom-Up Dynamic Programming - O(m) Time and O(m) Space
The idea is to use Dynamic Programming where dp[i] stores the maximum revenue up to mile i. For each mile, either skip the current billboard or place it and add its revenue to the best valid previous revenue. Store the maximum of these two choices.
Working of Approach:
- Create a DP array of size m + 1.
- Process each mile one by one.
- If a billboard exists, compare taking and skipping it.
- Update the DP value accordingly.
- The final answer is stored at dp[m].
#include <iostream>
#include <vector>
using namespace std;
// Function to return maximum revenue.
int maxRevenue(int m, vector<int> &x, vector<int> &revenue, int t)
{
int n = x.size();
// Stores maximum revenue till every mile.
vector<int> dp(m + 1, 0);
// Pointer for billboard positions.
int idx = 0;
for (int mile = 1; mile <= m; mile++)
{
// No billboard at this mile.
if (idx >= n || x[idx] != mile)
{
dp[mile] = dp[mile - 1];
}
else
{
// Ignore current billboard.
int skip = dp[mile - 1];
// Place current billboard.
int take = revenue[idx];
if (mile > t)
take += dp[mile - t - 1];
dp[mile] = max(skip, take);
idx++;
}
}
return dp[m];
}
int main()
{
int m = 20;
vector<int> x = {6, 7, 12, 13, 14};
vector<int> revenue = {5, 6, 5, 3, 1};
int t = 5;
cout << maxRevenue(m, x, revenue, t);
return 0;
}
import java.util.Arrays;
public class GFG {
// Function to return maximum revenue.
public static int maxRevenue(int m, int[] x,
int[] revenue, int t)
{
int n = x.length;
// Stores maximum revenue till every mile.
int[] dp = new int[m + 1];
// Pointer for billboard positions.
int idx = 0;
for (int mile = 1; mile <= m; mile++) {
// No billboard at this mile.
if (idx >= n || x[idx] != mile) {
dp[mile] = dp[mile - 1];
}
else {
// Ignore current billboard.
int skip = dp[mile - 1];
// Place current billboard.
int take = revenue[idx];
if (mile > t)
take += dp[mile - t - 1];
dp[mile] = Math.max(skip, take);
idx++;
}
}
return dp[m];
}
public static void main(String[] args)
{
int m = 20;
int[] x = { 6, 7, 12, 13, 14 };
int[] revenue = { 5, 6, 5, 3, 1 };
int t = 5;
System.out.println(maxRevenue(m, x, revenue, t));
}
}
def maxRevenue(m, x, revenue, t):
n = len(x)
# Stores maximum revenue till every mile.
dp = [0] * (m + 1)
# Pointer for billboard positions.
idx = 0
for mile in range(1, m + 1):
# No billboard at this mile.
if idx >= n or x[idx] != mile:
dp[mile] = dp[mile - 1]
else:
# Ignore current billboard.
skip = dp[mile - 1]
# Place current billboard.
take = revenue[idx]
if mile > t:
take += dp[mile - t - 1]
dp[mile] = max(skip, take)
idx += 1
return dp[m]
if __name__ == '__main__':
m = 20
x = [6, 7, 12, 13, 14]
revenue = [5, 6, 5, 3, 1]
t = 5
print(maxRevenue(m, x, revenue, t))
using System;
public class GFG {
// Function to return maximum revenue.
public static int maxRevenue(int m, int[] x,
int[] revenue, int t)
{
int n = x.Length;
// Stores maximum revenue till every mile.
int[] dp = new int[m + 1];
// Pointer for billboard positions.
int idx = 0;
for (int mile = 1; mile <= m; mile++) {
// No billboard at this mile.
if (idx >= n || x[idx] != mile) {
dp[mile] = dp[mile - 1];
}
else {
// Ignore current billboard.
int skip = dp[mile - 1];
// Place current billboard.
int take = revenue[idx];
if (mile > t)
take += dp[mile - t - 1];
dp[mile] = Math.Max(skip, take);
idx++;
}
}
return dp[m];
}
public static void Main()
{
int m = 20;
int[] x = { 6, 7, 12, 13, 14 };
int[] revenue = { 5, 6, 5, 3, 1 };
int t = 5;
Console.WriteLine(maxRevenue(m, x, revenue, t));
}
}
function maxRevenue(m, x, revenue, t)
{
let n = x.length;
// Stores maximum revenue till every mile.
let dp = Array(m + 1).fill(0);
// Pointer for billboard positions.
let idx = 0;
for (let mile = 1; mile <= m; mile++) {
// No billboard at this mile.
if (idx >= n || x[idx] != mile) {
dp[mile] = dp[mile - 1];
}
else {
// Ignore current billboard.
let skip = dp[mile - 1];
// Place current billboard.
let take = revenue[idx];
if (mile > t)
take += dp[mile - t - 1];
dp[mile] = Math.max(skip, take);
idx++;
}
}
return dp[m];
}
// Driver Code
let m = 20;
let x = [ 6, 7, 12, 13, 14 ];
let revenue = [ 5, 6, 5, 3, 1 ];
let t = 5;
console.log(maxRevenue(m, x, revenue, t));
Output
10
Note: The previous approach processes every mile of the highway, which is inefficient when m is much larger than n. This can be optimized by performing DP only on the billboard positions and using binary search.
[Expected Approach] Using DP with Binary Search - O(n log n) Time and O(n) Space
The idea is to use Dynamic Programming on the billboard positions instead of every mile. For each billboard, use binary search to find the first billboard whose position is greater than x[i] + t. Then, either place the current billboard or skip it, and store the maximum revenue.
Working of Approach:
- Create a DP array where dp[i] stores the maximum revenue starting from billboard i.
- Traverse the billboards from right to left.
- Use binary search to find the first billboard whose position is greater than x[i] + t.
- Compute the revenue by taking or skipping the current billboard.
- Store the maximum of these two choices.
Let us understand with an example:
Input: m = 20, x[] = [6, 7, 12, 13, 14], revenue[] = [5, 6, 5, 3, 1], t = 5
- Initialize dp = [0, 0, 0, 0, 0, 0] and process the billboards from right to left.
- At billboard 14, no valid next billboard exists, so dp[4] = max(0, 1) = 1. Similarly, dp[3] = max(1, 3) = 3 and dp[2] = max(3, 5) = 5.
- At billboard 7, the first billboard whose position is greater than 7 + 5 is 13, so dp[1] = max(5, 6 + 3) = 9.
- At billboard 6, the first billboard whose position is greater than 6 + 5 is 12, so dp[0] = max(9, 5 + 5) = 10.
- Thus, dp[0] = 10, which is the maximum revenue obtained by placing billboards at positions 6 and 12.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Function to return maximum revenue.
int maxRevenue(int m, vector<int> &x, vector<int> &revenue, int t)
{
int n = x.size();
// Stores maximum revenue starting from each billboard.
vector<int> dp(n + 1, 0);
// Process billboards from right to left.
for (int i = n - 1; i >= 0; i--)
{
// Find the next valid billboard using binary search.
int nextIndex = upper_bound(x.begin(), x.end(), x[i] + t) - x.begin();
// Skip current billboard.
int skip = dp[i + 1];
// Place current billboard.
int take = revenue[i] + dp[nextIndex];
dp[i] = max(skip, take);
}
return dp[0];
}
int main()
{
int m = 20;
vector<int> x = {6, 7, 12, 13, 14};
vector<int> revenue = {5, 6, 5, 3, 1};
int t = 5;
cout << maxRevenue(m, x, revenue, t);
return 0;
}
import java.util.*;
public class GFG {
// Returns first index having value > target.
static int upperBound(int[] arr, int target)
{
int low = 0, high = arr.length;
while (low < high) {
int mid = low + (high - low) / 2;
if (arr[mid] <= target)
low = mid + 1;
else
high = mid;
}
return low;
}
// Function to return maximum revenue.
static int maxRevenue(int m, int[] x, int[] revenue,
int t)
{
int n = x.length;
// Stores maximum revenue starting from each
// billboard.
int[] dp = new int[n + 1];
// Process billboards from right to left.
for (int i = n - 1; i >= 0; i--) {
// Find the next valid billboard.
int nextIndex = upperBound(x, x[i] + t);
// Skip current billboard.
int skip = dp[i + 1];
// Place current billboard.
int take = revenue[i] + dp[nextIndex];
dp[i] = Math.max(skip, take);
}
return dp[0];
}
public static void main(String[] args)
{
int m = 20;
int[] x = { 6, 7, 12, 13, 14 };
int[] revenue = { 5, 6, 5, 3, 1 };
int t = 5;
System.out.println(maxRevenue(m, x, revenue, t));
}
}
# Returns first index having value > target.
def upperBound(arr, target):
low = 0
high = len(arr)
while low < high:
mid = low + (high - low) // 2
if arr[mid] <= target:
low = mid + 1
else:
high = mid
return low
# Function to return maximum revenue.
def maxRevenue(m, x, revenue, t):
n = len(x)
# Stores maximum revenue starting from each billboard.
dp = [0] * (n + 1)
# Process billboards from right to left.
for i in range(n - 1, -1, -1):
# Find the next valid billboard.
nextIndex = upperBound(x, x[i] + t)
# Skip current billboard.
skip = dp[i + 1]
# Place current billboard.
take = revenue[i] + dp[nextIndex]
dp[i] = max(skip, take)
return dp[0]
if __name__ == "__main__":
m = 20
x = [6, 7, 12, 13, 14]
revenue = [5, 6, 5, 3, 1]
t = 5
print(maxRevenue(m, x, revenue, t))
using System;
class GFG {
// Returns first index having value > target.
static int UpperBound(int[] arr, int target)
{
int low = 0, high = arr.Length;
while (low < high) {
int mid = low + (high - low) / 2;
if (arr[mid] <= target)
low = mid + 1;
else
high = mid;
}
return low;
}
// Function to return maximum revenue.
static int MaxRevenue(int m, int[] x, int[] revenue,
int t)
{
int n = x.Length;
// Stores maximum revenue starting from each
// billboard.
int[] dp = new int[n + 1];
// Process billboards from right to left.
for (int i = n - 1; i >= 0; i--) {
// Find the next valid billboard.
int nextIndex = UpperBound(x, x[i] + t);
// Skip current billboard.
int skip = dp[i + 1];
// Place current billboard.
int take = revenue[i] + dp[nextIndex];
dp[i] = Math.Max(skip, take);
}
return dp[0];
}
static void Main()
{
int m = 20;
int[] x = { 6, 7, 12, 13, 14 };
int[] revenue = { 5, 6, 5, 3, 1 };
int t = 5;
Console.WriteLine(MaxRevenue(m, x, revenue, t));
}
}
function maxRevenue(m, x, revenue, t)
{
const n = x.length;
// Stores maximum revenue starting from each billboard.
let dp = new Array(n + 1).fill(0);
// Process billboards from right to left.
for (let i = n - 1; i >= 0; i--) {
// Find the next valid billboard using binary
// search.
let nextIndex = x.slice(i + 1).findIndex(
val => val > x[i] + t);
nextIndex
= nextIndex === -1 ? n : i + 1 + nextIndex;
// Skip current billboard.
let skip = dp[i + 1];
// Place current billboard.
let take = nextIndex < n
? revenue[i] + dp[nextIndex]
: revenue[i];
dp[i] = Math.max(skip, take);
}
return dp[0];
}
// Driver Code
const m = 20;
const x = [ 6, 7, 12, 13, 14 ];
const revenue = [ 5, 6, 5, 3, 1 ];
const t = 5;
console.log(maxRevenue(m, x, revenue, t));
Output
10