前段时间做了个vrptw(有时间窗车辆路径问题)的作业,用的算法是Solomon (1987)里1.3的插入启发算法。然后调用百度地图的接口在地图上显示路径。思路是用vrptw程序计算出路径输出保存到json文件中,然后上传json文件,解析,用百度地图接口将路径绘制出来。代码如下,仅供参考。
/*
LANG: C++
PROG: VRPTW
*/
#include <fstream>
#include <iostream>
#include <math.h>
#include <string>
#include <time.h>
using namespace std;
#define NUM 101 //custom数目(含depot)
#define CRITERIA_DIST 0 //初如化标准距离
#define CRITERIA_TIME 1 //初始化时间距离
typedef struct
{
double x, y, demand, rtime, dtime, stime;
double btime, wtime, c2;
int pos;
bool flag;
} cust;
const int mu[4] = { 1, 1, 1, 1 }; //c1, c2函数的参数
const int lambda[4] = { 1, 2, 1, 2 };
const int alpha1[4] = { 1, 1, 0, 0 };
const int alpha2[4] = { 0, 0, 1, 1 };
cust cset[NUM]; //custom集合
double distances[NUM][NUM]; //距离矩阵
int routes[NUM][NUM], num; //路径,路径条数
double mil, totalTime, cap, remain; //里程,总时间,容量,剩余货量
double tMil, tTime;
int tRoutes[NUM][NUM], tNum = NUM;
int criteria; //路径初始化标准
int index; //第几组参数
void vrptw(int count, int len);
void getResults(int count);
//求较大值
double max(double a, double b) {
if (a > b)
return a;
else
return b;
}
//读取数据
void readInData(ifstream &fin)
{
int no;
string s;
getline(fin, s);
for (int i = 0; i < NUM; i++) {
fin >> no;
fin >> cset[i].x >> cset[i].y //x, y坐标
>> cset[i].demand //需求量
>> cset[i].rtime >> cset[i].dtime //时间窗
>> cset[i].stime; //服务时间
cset[i].btime = 0; //服务开始时间
cset[i].wtime = 0; //等待时间
cset[i].flag = true; //标记是否routed
cset[i].pos = 0; //插入位置
cset[i].c2 = 0; //c2
}
fin >> cap;
}
//重置全局变量
void resetVariables()
{
int i, j;
if (num < tNum || (num == tNum && totalTime < tTime)) {
for (i = 0; i < num; i++) {
for (j = 0; j < NUM; j++)
tRoutes[i][j] = routes[i][j];
}
tNum = num;
tMil = mil;
tTime = totalTime;
}
for (i = 0; i < NUM; i++) {
cset[i].btime = 0;
cset[i].flag = true;
cset[i].pos = 0;
cset[i].c2 = 0;
}
for (i = 0; i < num; i++) {
for (j = 0; j < NUM; j++) {
routes[i][j] = 0;
}
}
totalTime = mil = num = 0;
}
//计算任意两custom间的距离
void calDistance(double distances[NUM][NUM])
{
for (int i = 0; i < NUM; i++) {
for (int j = 0; j < NUM; j++) {
double deltaX = cset[i].x - cset[j].x;
double deltaY = cset[i].y - cset[j].y;
distances[i][j] = sqrt(deltaX * deltaX + deltaY * deltaY);
}
}
}
//获得farthest unrouted custom
int getFarthestUcust()
{
int no = 0;
double distance = 0;
for (int i = 1; i < NUM; i++) {
if (cset[i].flag && distances[0][i] > distance) {
distance = distances[0][i];
no = i;
}
}
return no;
}
//获得earliest deadline custom
int getEarliestDcust()
{
int no = 0;
double deadline = 999999.0;
for (int i = 1; i < NUM; i++) {
if (cset[i].flag && cset[i].dtime < deadline) {
deadline = cset[i].dtime;
no = i;
}
}
return no;
}
//计算到达客户j的时间
double calArriveTime(int j, int i)
{
double pBtime;
if (i == 0)
pBtime = 0;
else
pBtime = cset[i].btime;
return (pBtime + cset[i].stime + distances[i][j] * 1);
}
//计算客户j的服务开始时间
double calBtime(int j, int i)
{
double arriveTime = calArriveTime(j, i);
return max(arriveTime, cset[j].rtime);
}
//计算客户j的等待时间
double calWtime(int j, int i)
{
double arriveTime = calArriveTime(j, i);
if (arriveTime >= cset[j].rtime)
return 0;
else
return cset[j].rtime - arriveTime;
}
//在count路径上的插入客户no
void insertCust(int count, int len, int no)
{
int i, ts;
for (i = len; i > cset[no].pos; i--) {
routes[count][i] = routes[count][i-1];
}
routes[count][i] = no;
cset[no].flag = false;
for (; i < len + 1; i++) {
ts = routes[count][i];
cset[ts].wtime = calWtime(ts, routes[count][i-1]);
cset[ts].btime = calBtime(ts, routes[count][i-1]);
}
remain -= cset[no].demand;
vrptw(count, len + 1);
}
//初始化一条新路径
void initialNewRoute(int count)
{
int custNo;
if (criteria == CRITERIA_DIST)
custNo = getFarthestUcust();
else
custNo = getEarliestDcust();
if (custNo != 0) {
routes[count][0] = 0;
routes[count][1] = 0;
cset[custNo].pos = 1;
remain = cap;
insertCust(count, 2, custNo);
}
else
getResults(count);
}
//c1函数
double func1(int count, int pos, int u)
{
int p, n;
double c11, c12;
p = routes[count][pos - 1];
n = routes[count][pos];
c11 = distances[p][u] + distances[u][n] - mu[index] * distances[p][n];
c12 = calBtime(n, u) - cset[n].btime;
return alpha1[index] * c11 + alpha2[index] * c12;
}
//c2函数
double func2(int u, double c1)
{
return lambda[index] * distances[0][u] - c1;
}
//验证是否满足time feasibility的充要条件
bool meetTCondition(int u, int pos, int count, int len)
{
double pf;
int prev, next, ts;
prev = routes[count][pos-1];
next = routes[count][pos];
cset[u].btime = calBtime(u, prev);
if (cset[u].btime > cset[u].dtime)
return false;
else {
pf = calBtime(next, u) - cset[next].btime;
if (cset[next].btime + pf > cset[next].dtime)
return false;
for (int r = pos + 1; r < len; r++) {
ts = routes[count][r];
pf = max(0, pf - cset[ts].wtime);
if (cset[ts].btime + pf > cset[ts].dtime)
return false;
}
return true;
}
}
//找最合适的插入位置
void findBestPos(int count, int len, int custNo)
{
int pos;
double c1, c2;
c1 = 999999.0;
for (int i = 1; i < len; i++) {
if (!meetTCondition(custNo, i, count, len) || remain < cset[custNo].demand)
continue;
else {
if (func1(count, i, custNo) < c1) {
c1 = func1(count, i, custNo);
pos = i;
}
}
}
if (c1 < 999999.0) {
c2 = func2(custNo, c1);
cset[custNo].pos = pos;
cset[custNo].c2 = c2;
}
}
//找最适合插入的custom
int findBestCust(int count, int len)
{
int custNo = 0;
double tmp = -999999.0;
for (int i = 1; i < NUM; i++) {
if (cset[i].flag) {
//先清除上次计算的数据
cset[i].pos = 0;
cset[i].c2 = 0;
findBestPos(count, len, i);
}
}
for (int i = 1; i < NUM; i++) {
if (cset[i].flag && cset[i].pos != 0) {
if (cset[i].c2 > tmp) {
tmp = cset[i].c2;
custNo = i;
}
}
}
return custNo;
}
//统计结果
void getResults(int count)
{
int i, j, a, b;
//double tmp = 0, time;
num = count;
for (i = 0; i < num; i++) {
j = 1;
while (j < 3 || routes[i][j - 1] != 0) {
a = routes[i][j - 1];
b = routes[i][j];
mil += distances[a][b];
j++;
}
totalTime += cset[a].btime + cset[a].stime + distances[a][b];
//time = cset[a].btime + cset[a].stime + distances[a][b];
//if (time > tmp)
//tmp = time;
}
//totalTime = tmp;
resetVariables();
}
//输出最终结果
void putOutData(ofstream &fout, ofstream &fjson, double runtime)
{
int i, j;
string sep;
double x, y;
fout.open("result.out");
fjson.open("routes.json");
fout << "tnum: " << tNum << endl
<< "tMil: " << tMil << endl
<< "runtime: " << runtime << endl;
fjson << "{" << endl;
fjson << "routes:" << endl << "[" << endl;
for (i = 0; i < tNum; i++) {
j = 0;
fjson << "[";
while (j < 2 || tRoutes[i][j] != 0) {
fjson << tRoutes[i][j] << ", ";
j++;
}
if (i == tNum - 1)
sep = "]";
else
sep = "],";
fjson << tRoutes[i][j] << sep << endl;
}
fjson << "]," << endl;
fjson << "cset:" << endl << "[" << endl;
for (i = 0; i < NUM; i++) {
if (i == NUM - 1)
sep = "]";
else
sep = "],";
//将x映射到114.0-114.5,y映射到22.5-23.0
x = cset[i].x / 100 * (114.5 - 114.0) + 114.0;
y = cset[i].y / 100 * (23.0 -22.5) + 22.5;
fjson << "[" << x << ", " << y << sep << endl;
}
fjson << "]" << endl << "}" << endl;
}
void vrptw(int count, int len)
{
int custNo;
if (count == 0 && len == 0)
initialNewRoute(count);
else {
custNo = findBestCust(count, len);
if (custNo == 0)
initialNewRoute(count + 1);
else
insertCust(count, len, custNo);
}
}
int main(int argc, char** argv)
{
ifstream fin;
ofstream fout, fjson;
clock_t start, finish;
start = clock();
if (argc != 2) {
cout << "Please use command like: vrptw c101.in." << endl;
return 1;
}
fin.open(argv[1]);
//fin.open("c201.in");
if (!fin.is_open()) {
cout << "Open file error! Application will exit." << endl;
return 2;
}
readInData(fin);
calDistance(distances);
for (int i = 0; i < 8; i++) {
criteria = i / 4;
index = i % 4;
vrptw(0, 0);
}
finish = clock();
putOutData(fout, fjson, (double)(finish - start) / CLOCKS_PER_SEC);
return 0;
}
调用百度地图接口
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;}
#l-map{height:100%;width:78%;float:left;border-right:2px solid #bcbcbc;}
#r-result{height:100%;width:20%;float:left;}
</style>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=88xDBIvgExozgbthhbFwuHlg"></script>
<title>VRPTW路径</title>
</head>
<body>
<input type="file" id="files" name="files[]" multiple />
<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">
function handleFileSelect(evt) {
var i, f;
var files = evt.target.files; // FileList object
// Loop through the FileList.
for (i = 0; f = files[i]; i++) {
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
var string = e.target.result;
var obj = eval ("(" + string + ")");
var routes = obj.routes;
var cset = obj.cset;
var points = new Array();
// 百度地图API功能
var map = new BMap.Map("allmap");
var cPoint = new BMap.Point(114.25, 22.75);
map.centerAndZoom(cPoint, 12);
// 创建标注
function addMarker(point) {
var marker = new BMap.Marker(point);
map.addOverlay(marker);
};
// 创建路径
function addPolyline(route, flag) {
var color;
if (flag == 0)
color = "blue";
else if (flag == 1)
color = "green";
else
color = "red"
var polyline = new BMap.Polyline(route, {strokeColor:color, strokeWeight:1.8, strokeOpacity:0.5});
map.addOverlay(polyline);
};
for (i = 0; i < 101; i++) {
points[i] = new BMap.Point(cset[i][0], cset[i][1]);
addMarker(points[i]);
}
for (i in routes) {
var route = new Array();
for (var j in routes[i]) {
route[j] = points[routes[i][j]];
}
addPolyline(route, i % 3);
}
};
})(f);
reader.readAsText(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>