/// <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 }); }
bool isOK(PlanData[] production, PlanData[,] sales, PlanData storageInfo) { if (production.Length != sales.GetLength(1)) { return(false); } if (production.Length <= 0) { return(false); } if (sales.GetLength(0) <= 0) { return(false); } if (storageInfo.Value < 0 || storageInfo.Quantity < 0) { return(false); } for (int i = 0; i < production.Length; ++i) { if (production[i].Quantity < 0 || production[i].Value < 0) { return(false); } for (int j = 0; j < sales.GetLength(0); ++j) { if (sales[j, i].Value < 0 || sales[j, i].Quantity < 0) { return(false); } } } return(true); }
bool isOK(PlanData[] production, PlanData[] sales, PlanData storageInfo) { if (production.Length != sales.Length) { return(false); } if (production.Length <= 0) { return(false); } if (sales.Length <= 0) { return(false); } if (storageInfo.Value < 0 || storageInfo.Quantity < 0) { return(false); } for (int i = 0; i < production.Length; ++i) { if (production[i].Quantity < 0 || production[i].Value < 0) { return(false); } if (sales[i].Value < 0 || sales[i].Quantity < 0) { return(false); } } return(true); }
/// <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) { int weeks = production.Length; if (weeks == 0 || storageInfo.Quantity < 0 || storageInfo.Value < 0 || weeks != sales.Length) { throw new ArgumentException(); } for (int i = 0; i < weeks; i++) { if (production[i].Quantity < 0 || production[i].Value < 0 || sales[i].Quantity < 0 || sales[i].Value < 0) { throw new ArgumentException(); } } Graph g = new AdjacencyListsGraph <HashTableAdjacencyList>(true, weeks + 2); Graph c = g.IsolatedVerticesGraph(); //n - source //n+1 - target for (int i = 0; i < weeks; i++) { g.AddEdge(weeks, i, production[i].Quantity); c.AddEdge(weeks, i, production[i].Value); g.AddEdge(i, weeks + 1, sales[i].Quantity); c.AddEdge(i, weeks + 1, -sales[i].Value); 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, weeks, weeks + 1, true, MaxFlowGraphExtender.FordFulkersonDinicMaxFlow, MaxFlowGraphExtender.MKMBlockingFlow); weeklyPlan = new SimpleWeeklyPlan[weeks]; for (int i = 0; i < weeks; i++) { weeklyPlan[i].UnitsProduced = (int)flow.GetEdgeWeight(weeks, i); weeklyPlan[i].UnitsSold = (int)flow.GetEdgeWeight(i, weeks + 1); if (i < weeks - 1) { weeklyPlan[i].UnitsStored = (int)flow.GetEdgeWeight(i, i + 1); } else { weeklyPlan[i].UnitsStored = 0; } } return(new PlanData { Quantity = (int)value, Value = -cost }); }
/// <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; return(new PlanData { Quantity = -1, Value = 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 }); }
/// <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 }); }
/// <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 }); }
/// <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) }); }
/// <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 }); }