前两天同学让我帮他解决一个数学建模作业问题,题目大致描述如下:
说某市有两个蔬菜生产基地,现在要从这两个生产基地生产蔬菜运送到各个该市的各个城镇。告诉你各个城镇之间的道路连接情况,包括道路长度。已知运输距离和运输成本成正相关,求一个生产方案使得运输成本最低。
输入如下:
城镇A编号 城镇B编号 道路长度
1 100 21.14
1 16 30.67
1 16 30.67
...
...
注:
1、生产基地分别在63号城镇和120号城镇。
2、需要蔬菜的城镇为: 20,106,63,31,141,10,65,79,1,120,36,27,34,42,94,11,24,63,145,22,16,123,64。
简单来说,就是求出各个城镇从哪个基地生产运输起来更便宜,是一个典型的有权最短路径问题。主要思路是,对于每个城镇,计算其到两个基地的最短距离。哪个距离短,就找哪个基地生产。
C++代码
#include <iostream>
#include <string>
#include <sstream>
#include <list>
#include <vector>
#include <map>
#include <utility>
#include <cfloat>
#include <iomanip>
using namespace std;
//表示生产基地的常量,避免magic number
const int START_CITY_A = 63;
const int START_CITY_B = 120;
// 在Dijkstra算法中保存顶点的数据结构
struct Vertex
{
vector<pair<int, double>> adjacent;//邻接编号和通往的距离
bool known; //是否被处理
double distance; //当前最短距离簿记
int path; //路径记录,指向距离自己最近的前一顶点
};
//读入输入数据,构建图
void creatMap(map<int,Vertex> & vertexs);
//Dijsktra核心实现
void useDijsktra(map<int,Vertex> & vertexs, int);
//Dijsktra算法所需要的子函数,查询未处理顶点和最近顶点
bool thereIsUnown(map<int,Vertex> & vertexs);
int findMinDisUnknown(map<int,Vertex> & vertexs);
//打印路径结果
void printResult(map<int,Vertex> & vertex, int city);
int main()
{
//第一步,用邻接表构建一个图
map<int,Vertex> vertexsA;
creatMap(vertexsA);
//由于要比较路径先做一个备份图
map<int,Vertex> vertexsB = vertexsA;
//对两张图应用Dijsktra算法
useDijsktra(vertexsA, START_CITY_A);
useDijsktra(vertexsB, START_CITY_B);
//需要蔬菜的城镇编号
int chain[] = {120,106,63,31,141,10,65,79,1,120,36,27,34,42,94,11,24,63,145,22,16,123,64};
for(auto it = begin(chain); it != end(chain); ++it)
{
if(vertexsA[*it].distance >= vertexsB[*it].distance)
{
cout<<"连锁店地点" << *it << '\t' << "生产地点:\t" << START_CITY_B << '\t' << "总距离:\t" << setprecision(3) << std::fixed << vertexsB[*it].distance << " \t路径\t";
printResult(vertexsB, *it);
cout << endl;
}
else
{
cout<<"连锁店地点" << *it << '\t' << "生产地点:\t" << START_CITY_A << '\t' << "总距离:\t" << setprecision(3) << std::fixed << vertexsA[*it].distance << " \t路径\t";
printResult(vertexsA, *it);
cout << endl;
}
}
return 0;
}
void creatMap(map<int,Vertex> & vertexs)
{
string forInputUse;
while(getline(cin,forInputUse))
{
istringstream is(forInputUse);
int startPoint, endPoint;
double distance;
is >> startPoint >> endPoint >> distance;
vertexs[startPoint].adjacent.push_back(pair<int, double>(endPoint, distance));
vertexs[endPoint].adjacent.push_back(pair<int, double>(startPoint, distance));
vertexs[startPoint].distance = vertexs[endPoint].distance = DBL_MAX;
vertexs[startPoint].known = vertexs[endPoint].known = false;
vertexs[startPoint].path = vertexs[endPoint].path = 0;
}
}
void useDijsktra(map<int,Vertex> & vertexs, int startPoint)
{
vertexs[startPoint].distance = 0;
while(thereIsUnown(vertexs))
{
int minDisUnknown = findMinDisUnknown(vertexs);
vertexs[minDisUnknown].known = true;
for(auto it = vertexs[minDisUnknown].adjacent.begin(); it != vertexs[minDisUnknown].adjacent.end(); ++it)
{
if(vertexs[(*it).first].known == false)
{
double distIncre = vertexs[minDisUnknown].distance + (*it).second;
if(distIncre < vertexs[(*it).first].distance)
{
vertexs[(*it).first].distance = distIncre;
vertexs[(*it).first].path = minDisUnknown;
}
}
}
}
}
bool thereIsUnown(map<int,Vertex> & vertexs)
{
for(auto it = vertexs.begin(); it != vertexs.end(); ++it)
{
if((*it).second.known == false)
return true;
}
return false;
}
int findMinDisUnknown(map<int,Vertex> & vertexs)
{
int minUnknown = 0;
double currentValue = DBL_MAX;
for(auto it = vertexs.begin(); it != vertexs.end(); ++it)
{
if((*it).second.known == false)
{
if((*it).second.distance < currentValue)
{
minUnknown = (*it).first;
currentValue = (*it).second.distance;
}
}
}
return minUnknown;
}
void printResult( map<int,Vertex> & vertex, int city)
{
cout << city << " ";
if(vertex[city].path != 0)
printResult(vertex, vertex[city].path);
}
运行结果
输入数据文件: https://pan.baidu.com/s/1-cm7uAAXBjKhGmFsOzS8KA 密码: kdb6
参考书籍:数据结构与算法分析——C++语言描述(第四版)