package inventory;

/**
 * Created on 09.04.2022 15:19:33
 *
 * @author Alexander Mikhailovich Kovshov
 */
public class InventoryModel1 {

    public static void main(String[] args) {

        int n = 8;                                           //число промежутков времени (месяцев)
        int max = 10;                                        //наибольшее значение для остатка на складе
        double[] inprice =  { 1,  1,  1,  2,  7, 10,  8,  6};//цены закупочные за каждый месяц              
        double[] outprice = {15, 10,  9, 10, 20, 30, 20, 10};//цены отпускные за каждый месяц           
        double[] store =    { 1,  0,  0,  0,  1,  1,  1,  1};//стоимость хранения единицы товара за каждый месяц
        int[]    need =     { 2,  6,  8,  6,  2,  2,  2,  2};//потребность за каждый месяц

        double[] profit = new double[max + 1];          //Наибольшая прибыль за все последующие месяцы в зависимости от остатка на конец текущего месяца.
        double[] profitTemp = new double[max + 1];   //Прибыль за текущий месяц и все последующие за ним месяцы в зависимости от остатка на начало месяца.
        int[][] restFromProfit = new int[n][max + 1];//Наилучший остаток на конец каждого месяца в зависимости от остатка на начало каждого месяца.

        //Вычисление возможной прибыли за конечный (n-й) месяц в зависимости от остатка на складе.
        //r --- возможный остаток на складе от предыдущего месяца
        for (int r = 0; r <= max; r++) {
            int add_r = need[n - 1] - r; //Приобретение за конечный (n-й) месяц.
            if (add_r < 0) {
                profit[r] = Integer.MIN_VALUE;
                continue;
            }
            profit[r]
                    = outprice[n - 1] * need[n - 1]
                    - inprice[n - 1] * add_r
                    - store[n - 1] * ((double) r / 2);
        }
        for (int m = n - 1; m > 0; m--) {
            //Вычисление возможной прибыли за m-й месяц в зависимости от остатка на складе.
            //r  --- возможный остаток на складе на начало месяца. 
            //rr --- возможный остаток на складе на конец месяца.
            for (int r = 0; r <= max; r++) {
                for (int rr = 0; rr <= max; rr++) {
                //Вычисление прибыли за m-й и последующие месяцы в зависимости от остатка на конец месяца.
                    //Приобретение за m-й месяц.
                    int add_r = need[m - 1] + rr - r;
                    if (add_r < 0) {
                        profitTemp[r] = Integer.MIN_VALUE;   //Отрицательное приобретение невозможно.
                    } else {
                        //Прибыль за этот и последующие месяцы в зависимости от остатков 
                        //на начало и конец этого месяца (r и rr).
                        double H
                                = outprice[m - 1] * need[m - 1]
                                - inprice[m - 1] * add_r
                                - store[m - 1] * ((double) (r + rr) / 2) + profit[rr];
                        //Если прибыль для данного остатка rr на конец месяца оказалась самой большой
                        //среди прибылей для других остатков на конец месяца при данном остатке r на
                        //начало месяца, то эта пока наибольшая прибыль сохраняется.
                        if (H > profitTemp[r]) {
                            profitTemp[r] = H;
                            restFromProfit[m - 1][r] = rr; //Сохраняется и пока наилучший остаток rr.
                        }
                    }
                }
            }
            //Значение наилучшей прибыли теперь будет включать и прибыль за текущий месяц m.
            for (int i = 0; i < profit.length; i++) {
                profit[i] = profitTemp[i];
            }
        }
        System.out.println("=========== План закупок ===============");
        for (int m = 0, r = 0; m < n; m++) {

            System.out.println((m + 1)
                    + "; Остаток на начало: " + r
                    + "; Остаток на конец: " + restFromProfit[m][r]
                    + "; Потребление: " + need[m]
                    + "; Закупки: " + (need[m] - r + restFromProfit[m][r])
            );
            r = restFromProfit[m][r];
        }
        System.out.println("Прибыль " + profit[0]);

    }
}