Exemplo n.º 1
0
        /// <summary>
        /// Część 1. zadania - zaplanowanie produkcji telewizorów dla pojedynczego kontrahenta.
        /// </summary>
        /// <remarks>
        /// Do przeprowadzenia testów wyznaczających maksymalną produkcję i 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ści pola <see cref="PlanData.Quantity"/> oznaczają limit produkcji w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - koszt produkcji jednej sztuki.
        /// </param>
        /// <param name="sales">
        /// Tablica obiektów zawierających informacje o sprzedaży w kolejnych tygodniach.
        /// Wartości pola <see cref="PlanData.Quantity"/> oznaczają maksymalną sprzedaż w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - cenę sprzedaży jednej sztuki.
        /// </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ę maksymalna liczba wyprodukowanych telewizorów,
        /// a w polu <see cref="PlanData.Value"/> - wyznaczony maksymalny zysk fabryki.
        /// </returns>
        public PlanData CreateSimplePlan(PlanData[] production, PlanData[] sales, PlanData storageInfo,
                                         out SimpleWeeklyPlan[] weeklyPlan)
        {
            weeklyPlan = null;
            if (!isOK(production, sales, storageInfo))
            {
                throw new ArgumentException();
            }
            int n = production.Length;

            weeklyPlan = new SimpleWeeklyPlan[n];
            for (int i = 0; i < n; ++i)
            {
                weeklyPlan[i].UnitsProduced = 0;
                weeklyPlan[i].UnitsSold     = 0;
                weeklyPlan[i].UnitsStored   = 0;
            }
            int   k = n + 2;
            int   source = 0; int sink = n + 1;
            Graph g = new AdjacencyListsGraph <SimpleAdjacencyList>(true, k);
            Graph r = new AdjacencyListsGraph <SimpleAdjacencyList>(true, k);


            for (int j = 0; j < n - 1; ++j)
            {
                g.AddEdge(new Edge(source, j + 1, production[j].Quantity)); // dodawanie produkcji      //produkcja+,   sales-,  magazynowanie-
                r.AddEdge(new Edge(source, j + 1, production[j].Value));

                g.AddEdge(new Edge(j + 1, sink, sales[j].Quantity)); // dodawanie sprzedazy
                r.AddEdge(new Edge(j + 1, sink, -sales[j].Value));

                g.AddEdge(new Edge(j + 1, j + 2, storageInfo.Quantity)); // dodawanie magazynu
                r.AddEdge(new Edge(j + 1, j + 2, storageInfo.Value));
            }

            g.AddEdge(source, n, production[n - 1].Quantity); // ostatni tydzien produkcji
            r.AddEdge(source, n, production[n - 1].Value);
            g.AddEdge(n, sink, sales[n - 1].Quantity);        // ostatni tydzien sprzedazy
            r.AddEdge(n, sink, -sales[n - 1].Value);
            (double value, double cost, Graph flow)ret = MinCostFlowGraphExtender.MinCostFlow(g, r, source, sink, false, MaxFlowGraphExtender.PushRelabelMaxFlow, null, false);
            int produced = 0;

            for (int i = 0; i < n; ++i)
            {
                produced += (int)ret.flow.GetEdgeWeight(source, i + 1);
                weeklyPlan[i].UnitsProduced = (int)ret.flow.GetEdgeWeight(source, i + 1);
                weeklyPlan[i].UnitsSold     = (int)ret.flow.GetEdgeWeight(i + 1, sink);
                if (i != n - 1)
                {
                    weeklyPlan[i].UnitsStored = (int)ret.flow.GetEdgeWeight(i + 1, i + 2);
                }
                else
                {
                    weeklyPlan[i].UnitsStored = 0;
                }
            }
            return(new PlanData {
                Quantity = produced, Value = -ret.cost
            });
        }
Exemplo n.º 2
0
        /// <summary>
        /// Część 1. zadania - zaplanowanie produkcji telewizorów dla pojedynczego kontrahenta.
        /// </summary>
        /// <remarks>
        /// Do przeprowadzenia testów wyznaczających maksymalną produkcję i 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ści pola <see cref="PlanData.Quantity"/> oznaczają limit produkcji w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - koszt produkcji jednej sztuki.
        /// </param>
        /// <param name="sales">
        /// Tablica obiektów zawierających informacje o sprzedaży w kolejnych tygodniach.
        /// Wartości pola <see cref="PlanData.Quantity"/> oznaczają maksymalną sprzedaż w danym tygodniu,
        /// a pola <see cref="PlanData.Value"/> - cenę sprzedaży jednej sztuki.
        /// </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ę maksymalna liczba wyprodukowanych telewizorów,
        /// a w polu <see cref="PlanData.Value"/> - wyznaczony maksymalny zysk fabryki.
        /// </returns>
        public PlanData CreateSimplePlan(PlanData[] production, PlanData[] sales, PlanData storageInfo,
                                         out SimpleWeeklyPlan[] weeklyPlan)
        {
            //Check sanity
            if (storageInfo.Value < 0 || storageInfo.Quantity < 0 || production.Length == 0 || production.Length != sales.Length)
            {
                throw new ArgumentException();
            }

            //Create graphs for Capacities and Costs
            Graph Capacities = new AdjacencyListsGraph <SimpleAdjacencyList>(true, production.Length + 2);
            Graph Costs      = new AdjacencyListsGraph <SimpleAdjacencyList>(true, production.Length + 2);

            //Hard-wire IDs special vertices
            int source = production.Length;
            int target = production.Length + 1;

            //Populate graphs with edges
            for (int i = 0; i < production.Length; i++)
            {
                //Check sanity
                if (production[i].Value < 0 || production[i].Quantity < 0 || sales[i].Quantity < 0 || sales[i].Value < 0)
                {
                    throw new ArgumentException();
                }

                //For Capacities
                Capacities.AddEdge(source, i, production[i].Quantity);
                Capacities.AddEdge(i, target, sales[i].Quantity);

                //For Costs
                Costs.AddEdge(source, i, production[i].Value);
                Costs.AddEdge(i, target, -sales[i].Value);

                //Edge for magazine purposes
                if (i < production.Length - 1)
                {
                    Capacities.AddEdge(i, i + 1, storageInfo.Quantity);
                    Costs.AddEdge(i, i + 1, storageInfo.Value);
                }
            }

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

            //Construct weekly plan
            weeklyPlan = new SimpleWeeklyPlan[production.Length];
            for (int i = 0; i < production.Length; i++)
            {
                int unitsStored   = (int)flow.GetEdgeWeight(i, i + 1);
                int unitsSold     = (int)flow.GetEdgeWeight(i, target);
                int unitsProduced = (int)flow.GetEdgeWeight(source, i);

                weeklyPlan[i].UnitsStored   = unitsStored > 0 ?(int)flow.GetEdgeWeight(i, i + 1) : 0;
                weeklyPlan[i].UnitsSold     = unitsSold > 0 ? (int)flow.GetEdgeWeight(i, target) : 0;
                weeklyPlan[i].UnitsProduced = unitsProduced > 0 ? (int)flow.GetEdgeWeight(source, i) : 0;
            }

            return(new PlanData {
                Quantity = (int)value, Value = -cost
            });
        }
Exemplo 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)
        {
            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
            });
        }
Exemplo 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)
        {
            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)
            });
        }
Exemplo n.º 5
0
        /// <summary>
        /// Metoda zwraca największą możliwą do wyprodukowania liczbę smerfonów
        /// </summary>
        /// <param name="providers">Dostawcy</param>
        /// <param name="factories">Fabryki</param>
        /// <param name="distanceCostMultiplier">współczynnik kosztu przewozu</param>
        /// <param name="productionCost">Łączny koszt produkcji wszystkich smerfonów</param>
        /// <param name="transport">Tablica opisująca ilości transportowanych surowców miedzy poszczególnymi dostawcami i fabrykami</param>
        /// <param name="maximumProduction">Maksymalny rozmiar produkcji</param>
        public static double CalculateFlow(Provider[] providers, Factory[] factories, double distanceCostMultiplier, out double productionCost, out int[,] transport, int maximumProduction = int.MaxValue)
        {
            /*
             * dostawcy wejściowi:   [0; providers.Length-1]
             * fabryki główne:       [providers.Length;  providers.Length+factories.Length-1]
             * fabryki nadgodzinne:  [providers.Length+factories.Length;  providers.Length+2*factories.Length-1]
             */
            int   source     = providers.Length + 2 * factories.Length;
            int   postsource = source + 1;
            int   sink       = source + 2;
            Graph graph      = new AdjacencyMatrixGraph(true, providers.Length + 2 * factories.Length + 3);
            Graph costs      = new AdjacencyMatrixGraph(true, providers.Length + 2 * factories.Length + 3);

            //Dodawanie krawędzi od 3 wierzchołków: źródła, post-źródła i ujścia
            graph.Add(new Edge(source, postsource, maximumProduction));
            costs.Add(new Edge(source, postsource, 0.0));
            for (int p = 0; p < providers.Length; p++)
            {
                graph.Add(new Edge(postsource, p, providers[p].Capacity));
                costs.Add(new Edge(postsource, p, providers[p].Cost));
            }
            for (int f = 0; f < factories.Length; f++)
            {
                graph.Add(new Edge(providers.Length + f, sink, factories[f].Limit));
                graph.Add(new Edge(providers.Length + factories.Length + f, sink, int.MaxValue));
                costs.Add(new Edge(providers.Length + f, sink, factories[f].LowerCost));
                costs.Add(new Edge(providers.Length + factories.Length + f, sink, factories[f].HigherCost));
            }

            //Proste łączenie dostawców fabrykami głownymi
            for (int p = 0; p < providers.Length; p++)
            {
                for (int f = 0; f < factories.Length; f++)
                {
                    graph.Add(new Edge(p, providers.Length + f, int.MaxValue));
                    costs.Add(new Edge(p, providers.Length + f, Math.Ceiling(distanceCostMultiplier * Distance(providers[p], factories[f]))));
                }
            }

            //Łączenie głównych fabryk z fabrykami nadgodzinnymi
            for (int f = 0; f < factories.Length; f++)
            {
                graph.Add(new Edge(providers.Length + f, providers.Length + factories.Length + f, int.MaxValue));
                costs.Add(new Edge(providers.Length + f, providers.Length + factories.Length + f, 0.0));
            }


            Graph flows;
            var   result = MinCostFlowGraphExtender.MinCostFlow(graph, costs, source, sink, out productionCost, out flows);

            transport = new int[providers.Length, factories.Length];
            for (int i = 0; i < providers.Length; i++)
            {
                foreach (var e in flows.OutEdges(i))
                {
                    if (e.Weight != 0)
                    {
                        transport[i, e.To - providers.Length] = (int)e.Weight;
                    }
                }
            }

            return(result);
        }