Transportation problem: Difference between revisions

Content added Content deleted
m (→‎stepping stones: tidied up testset)
Line 797: Line 797:
10 30 20 20
10 30 20 20
30 40 35 45
30 40 35 45

Optimal solution input3.txt

- - - 14
- 9 - 1
10 - 5 -
- 5 7 -
- 1 - -

Total costs: 1000</pre>

=={{header|C#|C sharp}}==
{{trans|Java}}
<lang csharp>using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace TransportationProblem {
class Shipment {
public Shipment(double q, double cpu, int r, int c) {
Quantity = q;
CostPerUnit = cpu;
R = r;
C = c;
}

public double CostPerUnit { get; }

public double Quantity { get; set; }

public int R { get; }

public int C { get; }
}

class Program {
private static int[] demand;
private static int[] supply;
private static double[,] costs;
private static Shipment[,] matrix;

static void Init(string filename) {
string line;
using (StreamReader file = new StreamReader(filename)) {
line = file.ReadLine();
var numArr = line.Split();
int numSources = int.Parse(numArr[0]);
int numDestinations = int.Parse(numArr[1]);

List<int> src = new List<int>();
List<int> dst = new List<int>();

line = file.ReadLine();
numArr = line.Split();
for (int i = 0; i < numSources; i++) {
src.Add(int.Parse(numArr[i]));
}

line = file.ReadLine();
numArr = line.Split();
for (int i = 0; i < numDestinations; i++) {
dst.Add(int.Parse(numArr[i]));
}

// fix imbalance
int totalSrc = src.Sum();
int totalDst = dst.Sum();
if (totalSrc > totalDst) {
dst.Add(totalSrc - totalDst);
} else if (totalDst > totalSrc) {
src.Add(totalDst - totalSrc);
}

supply = src.ToArray();
demand = dst.ToArray();

costs = new double[supply.Length, demand.Length];
matrix = new Shipment[supply.Length, demand.Length];

for (int i = 0; i < numSources; i++) {
line = file.ReadLine();
numArr = line.Split();
for (int j = 0; j < numDestinations; j++) {
costs[i, j] = int.Parse(numArr[j]);
}
}
}
}

static void NorthWestCornerRule() {
for (int r = 0, northwest = 0; r < supply.Length; r++) {
for (int c = northwest; c < demand.Length; c++) {
int quantity = Math.Min(supply[r], demand[c]);
if (quantity > 0) {
matrix[r, c] = new Shipment(quantity, costs[r, c], r, c);

supply[r] -= quantity;
demand[c] -= quantity;

if (supply[r] == 0) {
northwest = c;
break;
}
}
}
}
}

static void SteppingStone() {
double maxReduction = 0;
Shipment[] move = null;
Shipment leaving = null;

FixDegenerateCase();

for (int r = 0; r < supply.Length; r++) {
for (int c = 0; c < demand.Length; c++) {
if (matrix[r, c] != null) {
continue;
}

Shipment trial = new Shipment(0, costs[r, c], r, c);
Shipment[] path = GetClosedPath(trial);

double reduction = 0;
double lowestQuantity = int.MaxValue;
Shipment leavingCandidate = null;

bool plus = true;
foreach (var s in path) {
if (plus) {
reduction += s.CostPerUnit;
} else {
reduction -= s.CostPerUnit;
if (s.Quantity < lowestQuantity) {
leavingCandidate = s;
lowestQuantity = s.Quantity;
}
}
plus = !plus;
}
if (reduction < maxReduction) {
move = path;
leaving = leavingCandidate;
maxReduction = reduction;
}
}
}

if (move != null) {
double q = leaving.Quantity;
bool plus = true;
foreach (var s in move) {
s.Quantity += plus ? q : -q;
matrix[s.R, s.C] = s.Quantity == 0 ? null : s;
plus = !plus;
}
SteppingStone();
}
}

static List<Shipment> MatrixToList() {
List<Shipment> newList = new List<Shipment>();
foreach (var item in matrix) {
if (null != item) {
newList.Add(item);
}
}
return newList;
}

static Shipment[] GetClosedPath(Shipment s) {
List<Shipment> path = MatrixToList();
path.Add(s);

// remove (and keep removing) elements that do not have a
// vertical AND horizontal neighbor
int before;
do {
before = path.Count;
path.RemoveAll(ship => {
var nbrs = GetNeighbors(ship, path);
return nbrs[0] == null || nbrs[1] == null;
});
} while (before != path.Count);

// place the remaining elements in the correct plus-minus order
Shipment[] stones = path.ToArray();
Shipment prev = s;
for (int i = 0; i < stones.Length; i++) {
stones[i] = prev;
prev = GetNeighbors(prev, path)[i % 2];
}
return stones;
}

static Shipment[] GetNeighbors(Shipment s, List<Shipment> lst) {
Shipment[] nbrs = new Shipment[2];
foreach (var o in lst) {
if (o != s) {
if (o.R == s.R && nbrs[0] == null) {
nbrs[0] = o;
} else if (o.C == s.C && nbrs[1] == null) {
nbrs[1] = o;
}
if (nbrs[0] != null && nbrs[1] != null) {
break;
}
}
}
return nbrs;
}

static void FixDegenerateCase() {
const double eps = double.Epsilon;
if (supply.Length + demand.Length - 1 != MatrixToList().Count) {
for (int r = 0; r < supply.Length; r++) {
for (int c = 0; c < demand.Length; c++) {
if (matrix[r, c] == null) {
Shipment dummy = new Shipment(eps, costs[r, c], r, c);
if (GetClosedPath(dummy).Length == 0) {
matrix[r, c] = dummy;
return;
}
}
}
}
}
}
static void PrintResult(string filename) {
Console.WriteLine("Optimal solution {0}\n", filename);
double totalCosts = 0;

for (int r = 0; r < supply.Length; r++) {
for (int c = 0; c < demand.Length; c++) {
Shipment s = matrix[r, c];
if (s != null && s.R == r && s.C == c) {
Console.Write(" {0,3} ", s.Quantity);
totalCosts += (s.Quantity * s.CostPerUnit);
} else {
Console.Write(" - ");
}
}
Console.WriteLine();
}
Console.WriteLine("\nTotal costs: {0}\n", totalCosts);
}

static void Main() {
foreach (var filename in new string[] { "input1.txt", "input2.txt", "input3.txt" }) {
Init(filename);
NorthWestCornerRule();
SteppingStone();
PrintResult(filename);
}
}
}
}</lang>
{{out}}
<pre>Optimal solution input1.txt

20 - 5
- 30 5

Total costs: 180

Optimal solution input2.txt

- - - 12
20 - 10 10
- 30 - 3

Total costs: 130


Optimal solution input3.txt
Optimal solution input3.txt