Ejemplo n.º 1
0
        /// <summary>
        /// Część 2. zadania - zaplanowanie produkcji telewizorów dla wielu kontrahentów.
        /// </summary>
        /// <remarks>
        /// Do przeprowadzenia testów wyznaczających produkcję dającą maksymalny zysk wymagane jest jedynie zwrócenie obiektu <see cref="PlanData"/>.
        /// Testy weryfikujące plan wymagają przypisania tablicy z planem do parametru wyjściowego <see cref="weeklyPlan"/>.
        /// </remarks>
        /// <param name="production">
        /// Tablica obiektów zawierających informacje o produkcji fabryki w kolejnych tygodniach.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza limit produkcji w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - koszt produkcji jednej sztuki.
        /// </param>
        /// <param name="sales">
        /// Dwuwymiarowa tablica obiektów zawierających informacje o sprzedaży w kolejnych tygodniach.
        /// Pierwszy wymiar tablicy jest równy liczbie kontrahentów, zaś drugi - liczbie tygodni w planie.
        /// Wartości pola <see cref="PlanData.Quantity"/> oznaczają maksymalną sprzedaż w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - cenę sprzedaży jednej sztuki.
        /// Każdy wiersz tablicy odpowiada jednemu kontrachentowi.
        /// </param>
        /// <param name="storageInfo">
        /// Obiekt zawierający informacje o magazynie.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza pojemność magazynu,
        /// a pola <see cref="PlanData.Value"/> - koszt przechowania jednego telewizora w magazynie przez jeden tydzień.
        /// </param>
        /// <param name="weeklyPlan">
        /// Parametr wyjściowy, przez który powinien zostać zwrócony szczegółowy plan sprzedaży.
        /// </param>
        /// <returns>
        /// Obiekt <see cref="PlanData"/> opisujący wyznaczony plan.
        /// W polu <see cref="PlanData.Quantity"/> powinna znaleźć się optymalna liczba wyprodukowanych telewizorów,
        /// a w polu <see cref="PlanData.Value"/> - wyznaczony maksymalny zysk fabryki.
        /// </returns>
        public PlanData CreateComplexPlan(PlanData[] production, PlanData[,] sales, PlanData storageInfo,
                                          out WeeklyPlan[] weeklyPlan)
        {
            int   verticesPerWeek = 2 + sales.GetLength(0);
            int   verticesCount   = verticesPerWeek * production.Length + 2;
            Graph initialGraph    = new AdjacencyListsGraph <HashTableAdjacencyList>(true, verticesCount);
            Graph flowsGraph      = initialGraph.IsolatedVerticesGraph();
            Graph costGraph       = initialGraph.IsolatedVerticesGraph();

            for (int week = 0; week < production.Length; week++)
            {
                flowsGraph.AddEdge(0, week * verticesPerWeek + 1, double.PositiveInfinity);
                costGraph.AddEdge(0, week * verticesPerWeek + 1, 0);

                flowsGraph.AddEdge(week * verticesPerWeek + 1, week * verticesPerWeek + 2, production[week].Quantity);
                costGraph.AddEdge(week * verticesPerWeek + 1, week * verticesPerWeek + 2, production[week].Value);


                if (production.Length - week >= 2)
                {
                    flowsGraph.AddEdge(week * verticesPerWeek + 2, week * verticesPerWeek + verticesPerWeek + 2, storageInfo.Quantity);
                    costGraph.AddEdge(week * verticesPerWeek + 2, (week + 1) * verticesPerWeek + 2, storageInfo.Value);
                }
                //for (int i = 0; i < length; i++)
                //{

                //}
                //flowsGraph.AddEdge(week * verticesPerWeek + 2, week * verticesPerWeek + 3, sales[week].Quantity);
                //costGraph.AddEdge(week * verticesPerWeek + 2, week * verticesPerWeek + 3, -sales[week].Value);

                flowsGraph.AddEdge(week * verticesPerWeek + 3, verticesCount - 1, double.PositiveInfinity);
                costGraph.AddEdge(week * verticesPerWeek + 3, verticesCount - 1, 0);
            }


            (double tvQuantity, double cost, Graph resultFlow) = flowsGraph.MinCostFlow(costGraph, 0, verticesCount - 1, true);


            weeklyPlan = new WeeklyPlan[production.Length];
            for (int week = 0; week < production.Length; week++)
            {
                weeklyPlan[week].UnitsProduced = (int)resultFlow.GetEdgeWeight(week * verticesPerWeek + 1, week * verticesPerWeek + 2);
                //weeklyPlan[week].UnitsSold = (int)resultFlow.GetEdgeWeight(week * verticesPerWeek + 2, week * verticesPerWeek + 3);
                if (production.Length - week >= 2)
                {
                    weeklyPlan[week].UnitsStored = (int)resultFlow.GetEdgeWeight(week * verticesPerWeek + 2, (week + 1) * verticesPerWeek + 2);
                }
                else
                {
                    weeklyPlan[week].UnitsStored = 0;
                }
            }
            return(new PlanData {
                Quantity = (int)tvQuantity, Value = -cost
            });
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Część 2. zadania - zaplanowanie produkcji telewizorów dla wielu kontrahentów.
        /// </summary>
        /// <remarks>
        /// Do przeprowadzenia testów wyznaczających produkcję dającą maksymalny zysk wymagane jest jedynie zwrócenie obiektu <see cref="PlanData"/>.
        /// Testy weryfikujące plan wymagają przypisania tablicy z planem do parametru wyjściowego <see cref="weeklyPlan"/>.
        /// </remarks>
        /// <param name="production">
        /// Tablica obiektów zawierających informacje o produkcji fabryki w kolejnych tygodniach.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza limit produkcji w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - koszt produkcji jednej sztuki.
        /// </param>
        /// <param name="sales">
        /// Dwuwymiarowa tablica obiektów zawierających informacje o sprzedaży w kolejnych tygodniach.
        /// Pierwszy wymiar tablicy jest równy liczbie kontrahentów, zaś drugi - liczbie tygodni w planie.
        /// Wartości pola <see cref="PlanData.Quantity"/> oznaczają maksymalną sprzedaż w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - cenę sprzedaży jednej sztuki.
        /// Każdy wiersz tablicy odpowiada jednemu kontrachentowi.
        /// </param>
        /// <param name="storageInfo">
        /// Obiekt zawierający informacje o magazynie.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza pojemność magazynu,
        /// a pola <see cref="PlanData.Value"/> - koszt przechowania jednego telewizora w magazynie przez jeden tydzień.
        /// </param>
        /// <param name="weeklyPlan">
        /// Parametr wyjściowy, przez który powinien zostać zwrócony szczegółowy plan sprzedaży.
        /// </param>
        /// <returns>
        /// Obiekt <see cref="PlanData"/> opisujący wyznaczony plan.
        /// W polu <see cref="PlanData.Quantity"/> powinna znaleźć się optymalna liczba wyprodukowanych telewizorów,
        /// a w polu <see cref="PlanData.Value"/> - wyznaczony maksymalny zysk fabryki.
        /// </returns>
        public PlanData CreateComplexPlan(PlanData[] production, PlanData[,] sales, PlanData storageInfo,
                                          out WeeklyPlan[] weeklyPlan)
        {
            int c = sales.GetLength(0); //Contrahents no
            int n = sales.GetLength(1); //Weeks no

            //Check sanity
            if (storageInfo.Value < 0 || storageInfo.Quantity < 0 || c == 0 || n == 0 || n != production.Length)
            {
                throw new ArgumentException();
            }

            //Create graphs for Capacities and Costs
            Graph Capacities = new AdjacencyListsGraph <SimpleAdjacencyList>(true, 2 * n + c + 2);
            Graph Costs      = new AdjacencyListsGraph <SimpleAdjacencyList>(true, 2 * n + c + 2);

            //Vertices explanation:
            //n for n weeks
            //n for n magazines (one state for each week)
            //c for c contrahents
            //plus 2 extra vertices for source and target

            //Vertices definitions:

            //Source and targer
            int source = 2 * n + c + 1;
            int target = 2 * n + c;

            //Week main vertices
            int weeksStart = 0;

            //Week magazine vertices
            int weekMagStart = n;

            //Contrahents vertices
            int contrStart = 2 * n;

            //Populate edges going out of / going into week vertices
            for (int i = 0; i < n; i++)
            {
                //Check sanity
                if (production[i].Value < 0 || production[i].Quantity < 0)
                {
                    throw new ArgumentException();
                }

                int w = weeksStart + i; //current magazine week vertex
                int v = weekMagStart + i;

                //from source to v
                Capacities.AddEdge(source, w, production[i].Quantity);
                Costs.AddEdge(source, w, production[i].Value);

                //from v to target
                Capacities.AddEdge(w, target, production[i].Quantity);
                Costs.AddEdge(w, target, -production[i].Value);

                //from v to corresponding magazine
                Capacities.AddEdge(w, v, production[i].Quantity);
                Costs.AddEdge(w, v, 0);

                //Populate edges between magazine vertices
                if (i <= n - 2)
                {
                    int nextV = weekMagStart + i + 1;
                    Capacities.AddEdge(v, nextV, storageInfo.Quantity);
                    Costs.AddEdge(v, nextV, storageInfo.Value);
                }

                //Populate edges going out of week magazine vertices into contrahents' vertices
                for (int j = 0; j < c; j++)
                {
                    if (sales[j, i].Quantity < 0 || sales[j, i].Value < 0)
                    {
                        throw new ArgumentException();
                    }

                    int    con   = contrStart + j; //Contrahent vertex
                    int    quant = sales[j, i].Quantity;
                    double val   = sales[j, i].Value;

                    Capacities.AddEdge(v, con, quant);
                    Costs.AddEdge(v, con, -val);
                }
            }

            //Populate edges going from contrahents to sink (probably could be embedded into one of the 'main' previous loops, just testing for now
            for (int i = 0; i < c; i++)
            {
                int v = contrStart + i;
                Capacities.AddEdge(v, target, int.MaxValue);
                Costs.AddEdge(v, target, 0);
            }

            //Generate the flow
            (double value, double cost, Graph flow) = MinCostFlowGraphExtender.MinCostFlow(Capacities, Costs, source, target, true, MaxFlowGraphExtender.PushRelabelMaxFlow);

            //Calculate quantity produced
            weeklyPlan = new WeeklyPlan[n];
            int    quantity = 0;
            double profit   = 0;

            for (int i = 0; i < n; i++)
            {
                int week      = weeksStart + i;
                int magaz     = weekMagStart + i;
                int nextMagaz = weekMagStart + i + 1;

                int    itemsProduced        = (int)flow.GetEdgeWeight(week, magaz);
                double weeklyProductionCost = production[i].Value;
                weeklyPlan[i].UnitsProduced = itemsProduced;
                profit -= weeklyProductionCost * itemsProduced;

                int itemsStored = 0;
                if (i <= n - 2)
                {
                    itemsStored = (int)flow.GetEdgeWeight(magaz, nextMagaz);
                    profit     -= itemsStored * storageInfo.Value;
                }

                weeklyPlan[i].UnitsSold   = new int[c];
                weeklyPlan[i].UnitsStored = itemsStored;
                for (int j = 0; j < c; j++)
                {
                    int contr = contrStart + j;

                    if (i == 0)
                    {
                        quantity += (int)flow.GetEdgeWeight(contr, target);
                    }

                    int    itemsSold = (int)flow.GetEdgeWeight(magaz, contr);
                    double price     = sales[j, i].Value;

                    profit += price * itemsSold;
                    weeklyPlan[i].UnitsSold[j] = itemsSold;
                }
            }

            return(new PlanData {
                Quantity = quantity, Value = profit
            });
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Część 2. zadania - zaplanowanie produkcji telewizorów dla wielu kontrahentów.
        /// </summary>
        /// <remarks>
        /// Do przeprowadzenia testów wyznaczających produkcję dającą maksymalny zysk wymagane jest jedynie zwrócenie obiektu <see cref="PlanData"/>.
        /// Testy weryfikujące plan wymagają przypisania tablicy z planem do parametru wyjściowego <see cref="weeklyPlan"/>.
        /// </remarks>
        /// <param name="production">
        /// Tablica obiektów zawierających informacje o produkcji fabryki w kolejnych tygodniach.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza limit produkcji w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - koszt produkcji jednej sztuki.
        /// </param>
        /// <param name="sales">
        /// Dwuwymiarowa tablica obiektów zawierających informacje o sprzedaży w kolejnych tygodniach.
        /// Pierwszy wymiar tablicy jest równy liczbie kontrahentów, zaś drugi - liczbie tygodni w planie.
        /// Wartości pola <see cref="PlanData.Quantity"/> oznaczają maksymalną sprzedaż w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - cenę sprzedaży jednej sztuki.
        /// Każdy wiersz tablicy odpowiada jednemu kontrachentowi.
        /// </param>
        /// <param name="storageInfo">
        /// Obiekt zawierający informacje o magazynie.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza pojemność magazynu,
        /// a pola <see cref="PlanData.Value"/> - koszt przechowania jednego telewizora w magazynie przez jeden tydzień.
        /// </param>
        /// <param name="weeklyPlan">
        /// Parametr wyjściowy, przez który powinien zostać zwrócony szczegółowy plan sprzedaży.
        /// </param>
        /// <returns>
        /// Obiekt <see cref="PlanData"/> opisujący wyznaczony plan.
        /// W polu <see cref="PlanData.Quantity"/> powinna znaleźć się optymalna liczba wyprodukowanych telewizorów,
        /// a w polu <see cref="PlanData.Value"/> - wyznaczony maksymalny zysk fabryki.
        /// </returns>
        public PlanData CreateComplexPlan(PlanData[] production, PlanData[,] sales, PlanData storageInfo,
                                          out WeeklyPlan[] weeklyPlan)
        {
            weeklyPlan = null;
            if (!isOK(production, sales, storageInfo))
            {
                throw new ArgumentException();
            }

            int count = sales.GetLength(0); // count ilosc kotrahentow
            int n     = production.Length;  // n ilosc tygodni

            weeklyPlan = new WeeklyPlan[n];
            for (int i = 0; i < n; ++i)
            {
                weeklyPlan[i].UnitsSold = new int[count];
            }
            int source = n;
            int sink   = n + 1;
            int start  = n + 2;             // nr pierwszego kontrahenta
            int end    = start + count - 1; // nr ostatniego kontrahenta

            int V = 2 + count + n + n;

            int storageStart = end + 1;
            int storageEnd   = end + n;

            Graph g = new AdjacencyListsGraph <SimpleAdjacencyList>(true, V);
            Graph r = new AdjacencyListsGraph <SimpleAdjacencyList>(true, V);

            for (int i = start; i <= end; ++i)
            {
                // ujscie od kontrahenta
                g.AddEdge(i, sink, Double.MaxValue);//Double.MaxValue);
                r.AddEdge(i, sink, 0);
                // polaczenie sprzedazy kontrahentom
                int buyer = i - start;
                for (int tydzien = 0; tydzien < n; ++tydzien)
                {
                    g.AddEdge(storageStart + tydzien, i, sales[buyer, tydzien].Quantity);
                    r.AddEdge(storageStart + tydzien, i, -sales[buyer, tydzien].Value);
                }
            }
            for (int j = 0; j < n; ++j)
            {
                // dodanie produkcji (ze zrodla do tygodnia)
                g.AddEdge(source, j, production[j].Quantity);
                r.AddEdge(source, j, production[j].Value);
                // dodanie ujscia dla produkcji, ktorej sie nie oplaca produkowac
                g.AddEdge(j, sink, Double.MaxValue);
                r.AddEdge(j, sink, -production[j].Value);

                // dodanie krawedzi miedzy produkcja a wierzcholkiem kontrolujacym wyjscie do kontrahentow
                g.AddEdge(j, storageStart + j, production[j].Quantity);
                r.AddEdge(j, storageStart + j, 0);

                // dodanie magazynu
                if (j != n - 1) // bez ostatniego dnia bo wtedy nic nie magazynujemy
                {
                    g.AddEdge(storageStart + j, storageStart + j + 1, storageInfo.Quantity);
                    r.AddEdge(storageStart + j, storageStart + j + 1, storageInfo.Value);
                }
            }

            (double value, double cost, Graph flow)ret = MinCostFlowGraphExtender.MinCostFlow(g, r, source, sink, false, MaxFlowGraphExtender.PushRelabelMaxFlow, null, false);
            int    produced = 0;
            double profit   = 0;

            for (int i = 0; i < n; ++i)
            {
                // produkcja
                produced += weeklyPlan[i].UnitsProduced = production[i].Quantity - (int)ret.flow.GetEdgeWeight(i, sink);
                profit   -= weeklyPlan[i].UnitsProduced * production[i].Value;
                // sprzedaz
                for (int j = start; j <= end; ++j)
                {
                    weeklyPlan[i].UnitsSold[j - start] = (int)ret.flow.GetEdgeWeight(storageStart + i, j);
                    profit += ret.flow.GetEdgeWeight(storageStart + i, j) * (sales[j - start, i].Value); // (weeklyPlan[i].UnitsSold[j - start] * (sales[j - start, i].Value));
                }
                // magazyn
                if (i != n - 1)
                {
                    weeklyPlan[i].UnitsStored = (int)ret.flow.GetEdgeWeight(storageStart + i, storageStart + i + 1);
                    profit -= weeklyPlan[i].UnitsStored * storageInfo.Value;
                }
            }
            return(new PlanData {
                Quantity = produced, Value = Math.Ceiling(profit)
            });
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Część 2. zadania - zaplanowanie produkcji telewizorów dla wielu kontrahentów.
        /// </summary>
        /// <remarks>
        /// Do przeprowadzenia testów wyznaczających produkcję dającą maksymalny zysk wymagane jest jedynie zwrócenie obiektu <see cref="PlanData"/>.
        /// Testy weryfikujące plan wymagają przypisania tablicy z planem do parametru wyjściowego <see cref="weeklyPlan"/>.
        /// </remarks>
        /// <param name="production">
        /// Tablica obiektów zawierających informacje o produkcji fabryki w kolejnych tygodniach.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza limit produkcji w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - koszt produkcji jednej sztuki.
        /// </param>
        /// <param name="sales">
        /// Dwuwymiarowa tablica obiektów zawierających informacje o sprzedaży w kolejnych tygodniach.
        /// Pierwszy wymiar tablicy jest równy liczbie kontrahentów, zaś drugi - liczbie tygodni w planie.
        /// Wartości pola <see cref="PlanData.Quantity"/> oznaczają maksymalną sprzedaż w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - cenę sprzedaży jednej sztuki.
        /// Każdy wiersz tablicy odpowiada jednemu kontrachentowi.
        /// </param>
        /// <param name="storageInfo">
        /// Obiekt zawierający informacje o magazynie.
        /// Wartość pola <see cref="PlanData.Quantity"/> oznacza pojemność magazynu,
        /// a pola <see cref="PlanData.Value"/> - koszt przechowania jednego telewizora w magazynie przez jeden tydzień.
        /// </param>
        /// <param name="weeklyPlan">
        /// Parametr wyjściowy, przez który powinien zostać zwrócony szczegółowy plan sprzedaży.
        /// </param>
        /// <returns>
        /// Obiekt <see cref="PlanData"/> opisujący wyznaczony plan.
        /// W polu <see cref="PlanData.Quantity"/> powinna znaleźć się optymalna liczba wyprodukowanych telewizorów,
        /// a w polu <see cref="PlanData.Value"/> - wyznaczony maksymalny zysk fabryki.
        /// </returns>
        public PlanData CreateComplexPlan(PlanData[] production, PlanData[,] sales, PlanData storageInfo,
                                          out WeeklyPlan[] weeklyPlan)
        {
            int weeks   = production.Length;
            int clients = sales.GetLength(0);

            if (weeks == 0 || clients == 0 || storageInfo.Quantity < 0 || storageInfo.Value < 0 || weeks != sales.GetLength(1))
            {
                throw new ArgumentException();
            }
            for (int i = 0; i < weeks; i++)
            {
                if (production[i].Quantity < 0 || production[i].Value < 0)
                {
                    throw new ArgumentException();
                }
                for (int clientNr = 0; clientNr < clients; clientNr++)
                {
                    if (sales[clientNr, i].Quantity < 0 || sales[clientNr, i].Value < 0)
                    {
                        throw new ArgumentException();
                    }
                }
            }

            Graph g = new AdjacencyListsGraph <HashTableAdjacencyList>(true, 2 * weeks + 1 + clients + 1);
            Graph c = g.IsolatedVerticesGraph();
            //weeks - weeks+clients-1 - clients
            //weeks+clients - source
            //weeks+clients + 1 - target

            int source            = weeks + clients;
            int target            = weeks + clients + 1;
            int storageStart      = weeks;
            int dummyStorageStart = weeks + clients + 2;

            for (int i = 0; i < weeks; i++)
            {
                g.AddEdge(source, dummyStorageStart + i, production[i].Quantity);
                c.AddEdge(source, dummyStorageStart + i, 0);

                g.AddEdge(dummyStorageStart + i, i, production[i].Quantity);
                c.AddEdge(dummyStorageStart + i, i, production[i].Value);

                g.AddEdge(dummyStorageStart + i, target, production[i].Quantity);
                c.AddEdge(dummyStorageStart + i, target, 0);

                for (int clientNr = 0; clientNr < clients; clientNr++)
                {
                    g.AddEdge(i, storageStart + clientNr, sales[clientNr, i].Quantity);
                    c.AddEdge(i, storageStart + clientNr, -sales[clientNr, i].Value);

                    g.AddEdge(storageStart + clientNr, target, double.MaxValue);
                    c.AddEdge(storageStart + clientNr, target, 0);
                }

                if (i < weeks - 1)
                {
                    g.AddEdge(i, i + 1, storageInfo.Quantity);
                    c.AddEdge(i, i + 1, storageInfo.Value);
                }
            }

            (double value, double cost, Graph flow) = g.MinCostFlow(c, source, target, true, MaxFlowGraphExtender.FordFulkersonDinicMaxFlow, MaxFlowGraphExtender.MKMBlockingFlow);

            for (int i = 0; i < weeks; i++)
            {
                value -= flow.GetEdgeWeight(dummyStorageStart + i, target);
            }

            weeklyPlan = new WeeklyPlan[weeks];
            for (int i = 0; i < weeks; i++)
            {
                weeklyPlan[i].UnitsProduced = (int)flow.GetEdgeWeight(dummyStorageStart + i, i);
                if (i < weeks - 1)
                {
                    weeklyPlan[i].UnitsStored = (int)flow.GetEdgeWeight(i, i + 1);
                }
                else
                {
                    weeklyPlan[i].UnitsStored = 0;
                }
                weeklyPlan[i].UnitsSold = new int[clients];
                for (int clientNr = 0; clientNr < clients; clientNr++)
                {
                    weeklyPlan[i].UnitsSold[clientNr] = (int)flow.GetEdgeWeight(i, weeks + clientNr);
                }
            }

            return(new PlanData {
                Quantity = (int)value, Value = -cost
            });
        }