using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
public int ID { get; set; }
public int X { get; set; }
public int Y {get; set; }
public int Demand {get; set; }
public Point ( int id, int x, int y, int demand)
public double DistanceToPoint (Point p)
var temp1 = Math.Pow((this.X - p.X), 2);
var temp2 = Math.Pow((this.Y - p.Y), 2);
var result = Math.Sqrt(temp1+temp2);
public class ResultingTour
public List<int> Tour {get;set;}
public int Capacity {get;set;}
public ResultingTour (List <int> tour, int capa)
public class FilehandlingAndShapeReading
public List<Point> PointList;
public static void Main (string [] args)
FilehandlingAndShapeReading fas = new FilehandlingAndShapeReading();
string [] GivendataNodes= {"NAME : E-n51-k5","COMMENT : (Christophides and Eilon, Min no of trucks: 5, Optimal value: 521)","TYPE : CVRP",
"DIMENSION : 51","EDGE_WEIGHT_TYPE : EUC_2D","CAPACITY : 160","NODE_COORD_SECTION","1 30 40","2 37 52","3 49 49",
"4 52 64","5 20 26","6 40 30","7 21 47","8 17 63","9 31 62","10 52 33","11 51 21","12 42 41","13 31 32","14 5 25",
"15 12 42","16 36 16","17 52 41","18 27 23","19 17 33","20 13 13","21 57 58","22 62 42","23 42 57","24 16 57",
"25 8 52", "26 7 38", "27 27 68","28 30 48","29 43 67","30 58 48","31 58 27","32 37 69","33 38 46","34 46 10","35 61 33","36 62 63",
"37 63 69","38 32 22","39 45 35","40 59 15","41 5 6","42 10 17","43 21 10","44 5 64","45 30 15","46 39 10",
"47 32 39","48 25 32","49 25 55","50 48 28","51 56 37"};
string [] GivendataDemand= {"DEMAND_SECTION","1 0","2 7","3 30","4 16","5 9","6 21","7 15","8 19","9 23","10 11","11 5","12 19","13 29",
"14 23","15 21","16 10","17 15","18 3","19 41","20 9","21 28","22 8","23 8","24 16","25 10","26 28","27 7",
"28 15","29 14","30 6","31 19","32 11","33 12","34 23","35 26","36 17","37 6","38 9","39 15","40 14","41 7",
"42 27","43 13","44 11","45 16","46 10","47 5","48 25","49 17","50 18","51 10"};
string [] GivendataDepot= {"DEPOT_SECTION"," 1","-1","EOF"};
PointList = new List<Point>();
Depot = new Point(0,1,-1,0);
for(int i=0; i< GivendataNodes.Length; i++)
string firstPosition = GivendataNodes[i].Split(new char[0],StringSplitOptions.RemoveEmptyEntries)[0];
if (int.TryParse(firstPosition, out j) == true)
int id = Int32.Parse(GivendataNodes[i].Split(new char[0],StringSplitOptions.RemoveEmptyEntries)[0]);
int X = Int32.Parse(GivendataNodes[i].Split(new char[0],StringSplitOptions.RemoveEmptyEntries)[1]);
int Y = Int32.Parse(GivendataNodes[i].Split(new char[0],StringSplitOptions.RemoveEmptyEntries)[2]);
Depot = new Point(id,X,Y,Demand);
PointList.Add(new Point(id,X,Y,Demand));
else if (firstPosition.Equals("CAPACITY"))
capa = Int32.Parse(GivendataNodes[i].Split(new char[0],StringSplitOptions.RemoveEmptyEntries)[2]);
for(int i=1; i < GivendataDemand.Length; i++)
int demand = Int32.Parse(GivendataDemand[i].Split(new char[0],StringSplitOptions.RemoveEmptyEntries)[1]);
PointList[i-2].Demand = demand;
public double CalculateSavingsValue(Point p1, Point p2){
double savings = p1.DistanceToPoint(Depot) + p2.DistanceToPoint(Depot) - p1.DistanceToPoint(p2);
public Dictionary<string, double> sortedSavingMap(){
Dictionary<string, double> dict = new Dictionary<string, double>();
for(int i = 0; i < PointList.Count; i++){
for(int j = 0; j < PointList.Count; j++){
double savings = this.CalculateSavingsValue(PointList[i],PointList[j]);
string key = (i+2).ToString() + "," + (j+2).ToString();
dict = dict.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
public void savingAlgorithm(){
List<ResultingTour> allTours = new List<ResultingTour>();
foreach(var i in PointList){
List<int> singleTour = new List<int>();
singleTour.Add(Depot.ID);
singleTour.Add(Depot.ID);
allTours.Add(new ResultingTour(singleTour, capa - i.Demand));
Dictionary<string, double> dict = sortedSavingMap();
string[] key = i.Key.Split(',');
int firstNode = Int32.Parse(key[0]);
int secondNode = Int32.Parse(key[1]);
int firstTourPosition = -1;
int secondTourPosition = -1;
bool secondStart = false;
for(int j = 0; j < allTours.Count; j++){
if(allTours[j].Tour.Contains(firstNode)){
int firstListIndex = allTours[j].Tour.IndexOf(firstNode);
if(allTours[j].Tour[firstListIndex-1] == 1){
}else if(allTours[j].Tour[firstListIndex+1] == 1){
if(allTours[j].Tour.Contains(secondNode)){
int secondListIndex = allTours[j].Tour.IndexOf(secondNode);
if(allTours[j].Tour[secondListIndex-1] == 1){
}else if(allTours[j].Tour[secondListIndex+1] == 1){
if(secondTourPosition == firstTourPosition){
if(secondEnd && firstStart){
if(allTours[secondTourPosition].Capacity - (160 - allTours[firstTourPosition].Capacity) > 0){
allTours[firstTourPosition].Tour.RemoveAt(0);
allTours[secondTourPosition].Tour.RemoveAt(allTours[secondTourPosition].Tour.Count - 1);
allTours[secondTourPosition].Tour.AddRange(allTours[firstTourPosition].Tour);
allTours[secondTourPosition].Capacity -= (160 - allTours[firstTourPosition].Capacity);
allTours.RemoveAt(firstTourPosition);
else if(secondStart && firstEnd){
if(allTours[firstTourPosition].Capacity - (160 - allTours[secondTourPosition].Capacity) > 0){
allTours[firstTourPosition].Tour.RemoveAt(allTours[firstTourPosition].Tour.Count - 1);
allTours[secondTourPosition].Tour.RemoveAt(0);
allTours[firstTourPosition].Tour.AddRange(allTours[secondTourPosition].Tour);
allTours[firstTourPosition].Capacity -= (160 - allTours[secondTourPosition].Capacity);
allTours.RemoveAt(secondTourPosition);
else if(secondStart && firstStart){
if(allTours[firstTourPosition].Capacity - (160 - allTours[secondTourPosition].Capacity) > 0){
allTours[firstTourPosition].Tour.RemoveAt(0);
allTours[secondTourPosition].Tour.RemoveAt(0);
allTours[firstTourPosition].Tour.Reverse();
allTours[firstTourPosition].Tour.AddRange(allTours[secondTourPosition].Tour);
allTours[firstTourPosition].Capacity -= (160 - allTours[secondTourPosition].Capacity);
allTours.RemoveAt(secondTourPosition);
else if((secondEnd && firstEnd)){
if(allTours[firstTourPosition].Capacity - (160 - allTours[secondTourPosition].Capacity) > 0){
allTours[firstTourPosition].Tour.RemoveAt(allTours[firstTourPosition].Tour.Count - 1);
allTours[secondTourPosition].Tour.RemoveAt(allTours[secondTourPosition].Tour.Count - 1);
allTours[secondTourPosition].Tour.Reverse();
allTours[firstTourPosition].Tour.AddRange(allTours[secondTourPosition].Tour);
allTours[firstTourPosition].Capacity -= (160 - allTours[secondTourPosition].Capacity);
allTours.RemoveAt(secondTourPosition);
for(int i = 0; i < allTours.Count; i++){
String output = "Tour "+ (i+1) +": 1, ";
distance += Depot.DistanceToPoint(PointList[allTours[i].Tour[1]-2]);
for(int j = 1; j < allTours[i].Tour.Count-2; j++){
output += allTours[i].Tour[j].ToString() + ", ";
distance += PointList[allTours[i].Tour[j]-2].DistanceToPoint(PointList[allTours[i].Tour[j+1]-2]);
output += (allTours[i].Tour[(allTours[i].Tour.Count - 2)]) + ", " + (1).ToString();
distance += PointList[allTours[i].Tour[(allTours[i].Tour.Count - 2)]-2].DistanceToPoint(Depot);
output += " | Load: "+ (160 - allTours[i].Capacity).ToString() + " | Length: " + distance;
Console.WriteLine(output);