<!-- Run on Opera 10.10 on windows -->
<!-- Copy input to textarea, then push go and wait a while, finally result are displayed in text field -->
<html>
<head>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript">
Array.prototype.clone = function(){
return Array.apply(null,this)
}
function solve_(idx, matrix, used, cmin, result, clen, cresult)
{
if(idx == cresult.length-1) {
clen += matrix[cresult[idx-1]][cresult[idx]];
if(clen < cmin) {
return [ clen, cresult ];
}
return [ cmin, result ];
} else {
for(var i=0;i<used.length;++i) {
if(used[i] == 0 && cresult[idx-1] != i) {
clen2 = clen + matrix[cresult[idx-1]][i];
if(clen2 < cmin) {
used[i] = 1;
cresult[idx] = i;
var res = solve_(idx+1, matrix, used, cmin, result, clen2, cresult);
cmin = res[0];
result = res[1].clone();
used[i] = 0;
}
}
}
return [ cmin, result ];
}
}
function solve(data, matrix)
{
// solve
var used = new Array(data.length);
for(var i=0;i<used.length;++i) used[i] = 0;
var cresult = new Array(data.length+1);
cresult[0] = 0;
cresult[cresult.length-1] = 0;
used[0] = 1;
var result = solve_(1, matrix, used, 1000000000000, [], 0, cresult);
var answer = '';
for(var i=0;i<result[1].length;++i) {
if(i!=0) answer+=' ';
answer += data[result[1][i]].name
}
document.getElementById('result').value = answer;
document.getElementById('length').value = result[0];
alert('finished');
}
function calc()
{
// parse input
var inputs = document.getElementById('input').value.split(/\s+/);
var data = new Array(inputs.length/3);
for(var i=0;i<inputs.length/3;++i) {
data[i] = { name: inputs[i*3], lat: inputs[i*3+1], lng: inputs[i*3+2] };
}
var matrix = {};
for(var i=0;i<data.length;++i) {
matrix[i] = {};
for(var j=0;j<data.length;++j) if(i!=j) matrix[i][j]=0;
}
// resolve distance
var directionsService = new google.maps.DirectionsService();
var requests = data.length * (data.length-1);
for(var i=0;i<data.length;++i) {
for(var j=0;j<data.length;++j) {
if(i!=j) {
directionsService.route(
{
origin: new google.maps.LatLng(data[i]['lat'], data[i]['lng']),
destination: new google.maps.LatLng(data[j]['lat'], data[j]['lng']),
travelMode: google.maps.DirectionsTravelMode.DRIVING
}
, (function(ii,jj) {
return function(res, status) {
if(status == google.maps.DirectionsStatus.OK) {
matrix[ii][jj] = res.routes[0].legs[0].distance.value;
--requests;
if(requests==0) solve(data, matrix);
}
}
})(i,j) // Just using i and j causes error because binded variable becomes 3
);
}
}
}
}
</script>
</head>
<body>
<textarea id="input" cols="50" rows="10"></textarea>
<input type="button" value="go" onClick="calc()"><br>
<input type="text" id="result" size="100"><br>
<input type="text" id="length" size="30">
</body>
</html>
PCEtLSBSdW4gb24gT3BlcmEgMTAuMTAgb24gd2luZG93cyAtLT4KPCEtLSBDb3B5IGlucHV0IHRvIHRleHRhcmVhLCB0aGVuIHB1c2ggZ28gYW5kIHdhaXQgYSB3aGlsZSwgZmluYWxseSByZXN1bHQgYXJlIGRpc3BsYXllZCBpbiB0ZXh0IGZpZWxkIC0tPgo8aHRtbD4KPGhlYWQ+CjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IgogICAgc3JjPSJodHRwOi8vbWFwcy5nb29nbGUuY29tL21hcHMvYXBpL2pzP3NlbnNvcj1mYWxzZSI+Cjwvc2NyaXB0Pgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+CkFycmF5LnByb3RvdHlwZS5jbG9uZSA9IGZ1bmN0aW9uKCl7CiAgICByZXR1cm4gQXJyYXkuYXBwbHkobnVsbCx0aGlzKQp9CgpmdW5jdGlvbiBzb2x2ZV8oaWR4LCBtYXRyaXgsIHVzZWQsIGNtaW4sIHJlc3VsdCwgY2xlbiwgY3Jlc3VsdCkKewogIGlmKGlkeCA9PSBjcmVzdWx0Lmxlbmd0aC0xKSB7CiAgICBjbGVuICs9IG1hdHJpeFtjcmVzdWx0W2lkeC0xXV1bY3Jlc3VsdFtpZHhdXTsKICAgIGlmKGNsZW4gPCBjbWluKSB7CiAgICAgIHJldHVybiBbIGNsZW4sIGNyZXN1bHQgXTsKICAgIH0KICAgIHJldHVybiBbIGNtaW4sIHJlc3VsdCBdOwogIH0gZWxzZSB7CiAgICBmb3IodmFyIGk9MDtpPHVzZWQubGVuZ3RoOysraSkgewogICAgICBpZih1c2VkW2ldID09IDAgJiYgY3Jlc3VsdFtpZHgtMV0gIT0gaSkgewogICAgICAgIGNsZW4yID0gY2xlbiArIG1hdHJpeFtjcmVzdWx0W2lkeC0xXV1baV07CiAgICAgICAgaWYoY2xlbjIgPCBjbWluKSB7CiAgICAgICAgICB1c2VkW2ldID0gMTsKICAgICAgICAgIGNyZXN1bHRbaWR4XSA9IGk7CiAgICAgICAgICB2YXIgcmVzID0gc29sdmVfKGlkeCsxLCBtYXRyaXgsIHVzZWQsIGNtaW4sIHJlc3VsdCwgY2xlbjIsIGNyZXN1bHQpOwogICAgICAgICAgY21pbiA9IHJlc1swXTsKICAgICAgICAgIHJlc3VsdCA9IHJlc1sxXS5jbG9uZSgpOwogICAgICAgICAgdXNlZFtpXSA9IDA7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4gWyBjbWluLCByZXN1bHQgXTsKICB9Cn0KCmZ1bmN0aW9uIHNvbHZlKGRhdGEsIG1hdHJpeCkKewovLyBzb2x2ZQogIHZhciB1c2VkID0gbmV3IEFycmF5KGRhdGEubGVuZ3RoKTsKICBmb3IodmFyIGk9MDtpPHVzZWQubGVuZ3RoOysraSkgdXNlZFtpXSA9IDA7CiAgdmFyIGNyZXN1bHQgPSBuZXcgQXJyYXkoZGF0YS5sZW5ndGgrMSk7CiAgY3Jlc3VsdFswXSA9IDA7CiAgY3Jlc3VsdFtjcmVzdWx0Lmxlbmd0aC0xXSA9IDA7CiAgdXNlZFswXSA9IDE7CiAgdmFyIHJlc3VsdCA9IHNvbHZlXygxLCBtYXRyaXgsIHVzZWQsIDEwMDAwMDAwMDAwMDAsIFtdLCAwLCBjcmVzdWx0KTsKCiAgdmFyIGFuc3dlciA9ICcnOwogIGZvcih2YXIgaT0wO2k8cmVzdWx0WzFdLmxlbmd0aDsrK2kpIHsKICAgIGlmKGkhPTApIGFuc3dlcis9JyAnOwogICAgYW5zd2VyICs9IGRhdGFbcmVzdWx0WzFdW2ldXS5uYW1lCiAgfQogIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdyZXN1bHQnKS52YWx1ZSA9IGFuc3dlcjsKICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbGVuZ3RoJykudmFsdWUgPSByZXN1bHRbMF07CiAgYWxlcnQoJ2ZpbmlzaGVkJyk7Cn0KCmZ1bmN0aW9uIGNhbGMoKQp7Ci8vIHBhcnNlIGlucHV0CiAgdmFyIGlucHV0cyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdpbnB1dCcpLnZhbHVlLnNwbGl0KC9ccysvKTsKICB2YXIgZGF0YSA9IG5ldyBBcnJheShpbnB1dHMubGVuZ3RoLzMpOwogIGZvcih2YXIgaT0wO2k8aW5wdXRzLmxlbmd0aC8zOysraSkgewogICAgZGF0YVtpXSA9IHsgbmFtZTogaW5wdXRzW2kqM10sIGxhdDogaW5wdXRzW2kqMysxXSwgbG5nOiBpbnB1dHNbaSozKzJdIH07CiAgfQoKICB2YXIgbWF0cml4ID0ge307CiAgZm9yKHZhciBpPTA7aTxkYXRhLmxlbmd0aDsrK2kpIHsKICAgIG1hdHJpeFtpXSA9IHt9OwogICAgZm9yKHZhciBqPTA7ajxkYXRhLmxlbmd0aDsrK2opIGlmKGkhPWopIG1hdHJpeFtpXVtqXT0wOwogIH0KCi8vIHJlc29sdmUgZGlzdGFuY2UKICB2YXIgZGlyZWN0aW9uc1NlcnZpY2UgPSBuZXcgZ29vZ2xlLm1hcHMuRGlyZWN0aW9uc1NlcnZpY2UoKTsKICB2YXIgcmVxdWVzdHMgPSBkYXRhLmxlbmd0aCAqIChkYXRhLmxlbmd0aC0xKTsKICBmb3IodmFyIGk9MDtpPGRhdGEubGVuZ3RoOysraSkgewogICAgZm9yKHZhciBqPTA7ajxkYXRhLmxlbmd0aDsrK2opIHsKICAgICAgaWYoaSE9aikgewoJZGlyZWN0aW9uc1NlcnZpY2Uucm91dGUoCiAgICAgICAgICB7CgkgICAgb3JpZ2luOiBuZXcgZ29vZ2xlLm1hcHMuTGF0TG5nKGRhdGFbaV1bJ2xhdCddLCBkYXRhW2ldWydsbmcnXSksCgkgICAgZGVzdGluYXRpb246IG5ldyBnb29nbGUubWFwcy5MYXRMbmcoZGF0YVtqXVsnbGF0J10sIGRhdGFbal1bJ2xuZyddKSwKCSAgICB0cmF2ZWxNb2RlOiBnb29nbGUubWFwcy5EaXJlY3Rpb25zVHJhdmVsTW9kZS5EUklWSU5HCgkgIH0KICAgICAgICAgICwgKGZ1bmN0aW9uKGlpLGpqKSB7CiAgICAgICAgICAgIHJldHVybiBmdW5jdGlvbihyZXMsIHN0YXR1cykgewogICAgICAgICAgICAgIGlmKHN0YXR1cyA9PSBnb29nbGUubWFwcy5EaXJlY3Rpb25zU3RhdHVzLk9LKSB7CiAgICAgICAgICAgICAgICBtYXRyaXhbaWldW2pqXSA9IHJlcy5yb3V0ZXNbMF0ubGVnc1swXS5kaXN0YW5jZS52YWx1ZTsKICAgICAgICAgICAgICAgIC0tcmVxdWVzdHM7CiAgICAgICAgICAgICAgICBpZihyZXF1ZXN0cz09MCkgc29sdmUoZGF0YSwgbWF0cml4KTsKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgIH0pKGksaikgLy8gSnVzdCB1c2luZyBpIGFuZCBqIGNhdXNlcyBlcnJvciBiZWNhdXNlIGJpbmRlZCB2YXJpYWJsZSBiZWNvbWVzIDMKICAgICAgICApOwogICAgICB9CiAgICB9CiAgfQp9Cjwvc2NyaXB0Pgo8L2hlYWQ+Cjxib2R5Pgo8dGV4dGFyZWEgaWQ9ImlucHV0IiBjb2xzPSI1MCIgcm93cz0iMTAiPjwvdGV4dGFyZWE+CjxpbnB1dCB0eXBlPSJidXR0b24iIHZhbHVlPSJnbyIgb25DbGljaz0iY2FsYygpIj48YnI+CjxpbnB1dCB0eXBlPSJ0ZXh0IiBpZD0icmVzdWx0IiBzaXplPSIxMDAiPjxicj4KPGlucHV0IHR5cGU9InRleHQiIGlkPSJsZW5ndGgiIHNpemU9IjMwIj4KPC9ib2R5Pgo8L2h0bWw+