package com.thealgorithms.dynamicprogramming;
/**
* The WineProblem class provides a solution to the wine selling problem.
* Given a collection of N wines with different prices, the objective is to maximize profit by selling
* one wine each year, considering the constraint that only the leftmost or rightmost wine can be sold
* at any given time.
*
* The price of the ith wine is pi, and the selling price increases by a factor of the year in which
* it is sold. This class implements three approaches to solve the problem:
*
* 1. **Recursion**: A straightforward recursive method that computes the maximum profit.
* - Time Complexity: O(2^N)
* - Space Complexity: O(N) due to recursive calls.
*
* 2. **Top-Down Dynamic Programming (Memoization)**: This approach caches the results of subproblems
* to avoid redundant computations.
* - Time Complexity: O(N^2)
* - Space Complexity: O(N^2) for the storage of results and O(N) for recursion stack.
*
* 3. **Bottom-Up Dynamic Programming (Tabulation)**: This method builds a table iteratively to
* compute the maximum profit for all possible subproblems.
* - Time Complexity: O(N^2)
* - Space Complexity: O(N^2) for the table.
*/
public final class WineProblem {
private WineProblem() {
}
/**
* Calculate maximum profit using recursion.
*
* @param arr Array of wine prices.
* @param si Start index of the wine to consider.
* @param ei End index of the wine to consider.
* @return Maximum profit obtainable by selling the wines.
*/
public static int wpRecursion(int[] arr, int si, int ei) {
int n = arr.length;
int year = (n - (ei - si + 1)) + 1;
if (si == ei) {
return arr[si] * year;
}
int start = wpRecursion(arr, si + 1, ei) + arr[si] * year;
int end = wpRecursion(arr, si, ei - 1) + arr[ei] * year;
return Math.max(start, end);
}
/**
* Calculate maximum profit using top-down dynamic programming with memoization.
*
* @param arr Array of wine prices.
* @param si Start index of the wine to consider.
* @param ei End index of the wine to consider.
* @param strg 2D array to store results of subproblems.
* @return Maximum profit obtainable by selling the wines.
*/
public static int wptd(int[] arr, int si, int ei, int[][] strg) {
int n = arr.length;
int year = (n - (ei - si + 1)) + 1;
if (si == ei) {
return arr[si] * year;
}
if (strg[si][ei] != 0) {
return strg[si][ei];
}
int start = wptd(arr, si + 1, ei, strg) + arr[si] * year;
int end = wptd(arr, si, ei - 1, strg) + arr[ei] * year;
int ans = Math.max(start, end);
strg[si][ei] = ans;
return ans;
}
/**
* Calculate maximum profit using bottom-up dynamic programming with tabulation.
*
* @param arr Array of wine prices.
* @throws IllegalArgumentException if the input array is null or empty.
* @return Maximum profit obtainable by selling the wines.
*/
public static int wpbu(int[] arr) {
if (arr == null || arr.length == 0) {
throw new IllegalArgumentException("Input array cannot be null or empty.");
}
int n = arr.length;
int[][] strg = new int[n][n];
for (int slide = 0; slide <= n - 1; slide++) {
for (int si = 0; si <= n - slide - 1; si++) {
int ei = si + slide;
int year = (n - (ei - si + 1)) + 1;
if (si == ei) {
strg[si][ei] = arr[si] * year;
} else {
int start = strg[si + 1][ei] + arr[si] * year;
int end = strg[si][ei - 1] + arr[ei] * year;
strg[si][ei] = Math.max(start, end);
}
}
}
return strg[0][n - 1];
}
}