学校超市选址问题
设计内容
设计内容:对于某一学校超市,其他各单位到其的距离不同,同时各单位人员去超市的频度也不同。请为超市选址,要求实现总体最优。
设计要求
- 设计该问题的核心算法;
- 设计可视化的界面,界面中能有效显示学校超市可设立的地点和各单位的位置以及它们之间的有效路径;
- 程序能自动计算出最优设立点,并最好以图示化方式演示。
设计核心
在我做这个算法题的时候,看了网上各种的文档设计。有人是将到各个地点到其他地点的最短距离之和求出,然后以到各个地点最短距离的地点为超市选址。我后面跟老师讨论后,我认为超市应该跟上面那个人的相反。应该是被到距离最短的地点作为超市,并且,由于这个题目涉及到路径和频率还有各个地点到其他地点的频率不一样,所以,意味着两个地点之间是有两条路线,路径是一样的,但是频率不一样。
涉及知识
佛洛依德算法,大家可以参考这个网站或者B站上看视频
https://www.cnblogs.com/wangyuliang/p/9216365.html
课程设计,大家可以看看这个
https://wenku.baidu.com/view/b3815caa0029bd64783e2c5e.html
然后,我做课设喜欢用头文件封装一下,所以大家可以看看下面这个链接,学一下怎么封装头文件
https://blog.youkuaiyun.com/weixin_44811417/article/details/90405819
我们课设要有可视化,然后,我之前也没有学过可视化,再加上期末考试,我原本想用Qt,但是时间紧迫就使用了轻量级别的可视化软件Graphviz,可以参考的链接
graphviz的官网
http://www.graphviz.org/
graphviz的安装
https://www.cnblogs.com/shuodehaoa/p/8667045.html
简书——如何使用graphviz
https://www.jianshu.com/p/6d9bbbbf38b1
相关代码
由于我忙着完成课设就去准备期末考试了,所以代码只是粗略地封装了。没有再仔细地封装,大家一定要仔细封装,要是不急的话,大家尽量仔细思考如何封装
然后我是习惯从文件中读取数据,这样也方便我多次测试。
弗洛伊德算法
int Graph::Floyed()
{
int i,j,k;
for(i =0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if((i!=j)&&(ArcsDis[i][j]!=-2))
Arcs[i][j] = j;
}
}
for(k = 0; k<Vexnum; k++)
{
for(i =0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if(ArcsDis[i][j]>(ArcsDis[i][k]+ArcsDis[k][j]))
{
if((ArcsDis[i][k]!=-1)&&(ArcsDis[i][k]!=-2)&&(ArcsDis[k][j]!=-1)&&(ArcsDis[k][j]!=-2))
{
ArcsDis[i][j] = ArcsDis[i][k]+ArcsDis[k][j];
Arcs[i][j] = k;
}
}
}
}
}
for(i =0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if ((i != j)&&(ArcsDis[i][j]!=-2))
{
cout << Vex[i] << "到" << Vex[j] << "最短路径权值为:"<<ArcsDis[i][j]<<"\t路径为:";
int next = Arcs[i][j];
cout<<Vex[i];
while(next!=j)
{
cout<<"->"<<Vex[next];
next = Arcs[next][j];
}
cout<<"->"<<Vex[j]<<endl;
}
}
}
return 0;
}
计算路径总和
int Graph::Compute(void)
{
int i,j;
for(i = 0; i<Vexnum; i++)
sum[i] = 0;
for(i = 0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if((i!=j)&&(Arcs[i][j]!=-2))
sum[i] += ArcsDis[j][i];
}
cout<<"各个顶点到"<<Vex[i]<<"点的最短路径为:"<<sum[i]<<endl;
}
temp_d = sum[0];
for(i = 0; i<Vexnum; i++)
{
if(temp_d>sum[i])
{
temp_d = sum[i];
temp_l = i;
}
}
cout<<"各单位到"<<Vex[temp_l]<<"顶点的距离最短"<<temp_d<<endl;
cout<<"学校超市最佳的地址为"<<Vex[temp_l]<<endl;
return 0;
}
运行结果
运行结果
用Graphviz软件画出的图
源代码
main.cpp
#include<iostream>
#include <stdlib.h>
#include <math.h>
#include"graph.h"
#include"file.h"
using namespace std;
int main(void)
{
char select;
int n;
do
{
cout<<"请选择输入方式?(1.手动输入\t2.自动输入\t0.退出):"<<endl;
cin>>select;
Graph a;
a.init();
if(select == '1')
a.CreateGraph();
else if(select == '2')
read(a);
else
exit(-1);
a.Floyed();
a.Compute();
write(a);
cout<<"请问是否继续(Y/N):"<<endl;
cin>>select;
}while((select=='Y')||(select=='y'));
return 0;
}
file
file.h
#ifndef FILE_H_INCLUDED
#define FILE_H_INCLUDED
int read(Graph &G);
int write(Graph G);
int Compare(Graph G,char input);
#endif // FILE_H_INCLUDED
file.cpp
#include<iostream>
#include<fstream>
#include<iomanip>
#include"graph.h"
#include"file.h"
/*
定义的输入、输出文件
*/
#define source "input.txt"
#define result "output.dot"
using namespace std;
int read(Graph &G)
{
int i,j,n,x,y,W,F;//坐标x,y 权值W 频率F
char x_c,y_c;
char interception[200];
cout<<"正在从文件读入数据,请稍等!\n";
ifstream infile(source, ios::in);
if (!infile)
{
cout << "打开文档失败!!" << endl;
exit(-1);
}
else
cout << "打开文档成功!!" << endl;
for(i=0; i<6; i++)
infile.getline(interception,200,'\n');
infile>>G.Vexnum;
n = G.Vexnum*(G.Vexnum - 1);
for(i=0; i<G.Vexnum; i++)
{
infile>>G.Vex[i];
}
for(i=0; i<G.Vexnum; i++)
{
infile>>W;
for(j=0; j<G.Vexnum; j++)
{
if(i!=j)
G.ArcsDis[i][j] = W;
}
}
for(i=0; i<n; i++)
{
infile>>x_c>>y_c>>F;
x = Compare(G,x_c);
y = Compare(G,y_c);
G.ArcsDis[x][y] *= F;
G.Arcs[x][y] = y;
}
infile.close();
return 0;
}
int write(Graph G)
{
ofstream outfile(result,ios::trunc|ios::out);
int i;
// outfile<<"邻接矩阵:"<<endl;
outfile<<"digraph pic{"<<endl;
outfile<<"x[shape = box,color = lightblue,style = filled,label = SuperMakert];"<<endl;
for(i = 0; i<G.Vexnum; i++)
{
if(i==G.temp_l)
outfile<<G.Vex[i]<<"[shape = box,color = lightblue,style = filled,label = "<<G.Vex[i]<<"];"<<endl;
else
outfile<<G.Vex[i]<<"[shape = box,label = "<<G.Vex[i]<<"];"<<endl;
}
for(int i=0; i<G.Vexnum; i++)
{
for(int j=0; j<G.Vexnum; j++)
{
if ((i != j)&&(G.ArcsDis[i][j]!=-2))
outfile<<G.Vex[i]<<"->"<<G.Vex[j]<<"[label ="<<G.ArcsDis[i][j]<<" ];"<<endl;
}
}
outfile<<"}"<<endl;
system("dot -Tpng output.dot -o Graph.png");
system("Start C:\\Users\\RainBowTeam\\Documents\\我的学习\\算法分析\\课程设计\\Knapsack_problem\\Graph.png");
outfile.close();
return 0;
}
int Compare(Graph G,char input)
{
int i;
for(i = 0; i<G.Vexnum; i++)
{
if(input == G.Vex[i])
break;
}
return i;
}
graph
graph.h
#ifndef GRAPH_H_INCLUDED
#define GRAPH_H_INCLUDED
#define MVNum 20//定义的最大数
/*图的基类*/
class Graph
{
public:
Graph(int a = 0,int b = 0,int c = 0,int d = 0):Vexnum(a),Arcnum(b),temp_d(0),temp_l(0)
{
}
void init(void);
int CreateGraph(void);
int Floyed(void);
int Compute(void);
int print(void);
int Compare(char input);
int Vexnum; //总顶点数
int Arcnum; //总边数
int temp_d;
int temp_l;
char Vex[MVNum]; //顶点表
int Arcs[MVNum][MVNum]; //连接点矩阵
int ArcsDis[MVNum][MVNum]; //距离矩阵
int sum[MVNum]; //路径总和
private:
};
#endif // GRAPH_H_INCLUDED
graph.cpp
#include<iostream>
#include<fstream>
#include<string>
#include<iomanip>
#include"graph.h"
using namespace std;
/*
Graph::init(void) 从文件中读入顶点信息并写入邻接矩阵
函数名(图名,数据总数)
函数描述:从文件中读入数据,并将边信息写入邻接矩阵
*/
void Graph::init(void)//邻接矩阵初始化读入数据
{
for(int i = 0; i<MVNum; i++)
{
for(int j =0; j<MVNum; j++)
{
Vex[i] = 0;
if(i == j)
{
ArcsDis[i][j] = -1;
Arcs[i][j] = -1;
}
else
{
ArcsDis[i][j] = -2;
Arcs[i][j] = -2;
}
}
}
}
/*
Graph::CreateAMGraph() 创建、显示、写入邻接矩阵
函数名(图名,数据总组数)
函数描述:在文本框中显示矩阵并且写入输出结果文件中
*/
int Graph::CreateGraph(void)//创建邻接矩阵
{
int x = 0,y = 0,dis = 0,f = 0;
char x_c,y_c;
cout<<"请问有几个单位:";
cin>>Vexnum;
for(int i = 0; i<Vexnum; i++)
{
cout<<"请输入,第"<<i+1<<"个单位的名称:";
cin>>Vex[i];
}
cout<<"请问有几条边:";
cin>>Arcnum;
for(int i = 0; i<Arcnum; i++)
{
cout<<"请输入出发单位到终止单位的频率及距离:";
cin>>x_c>>y_c>>dis>>f;
x = Compare(x_c);
y = Compare(y_c);
ArcsDis[x][y] = dis*f;
Arcs[x][y]=y;
}
return 0;
}
int Graph::Floyed()
{
int i,j,k;
for(i =0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if((i!=j)&&(ArcsDis[i][j]!=-2))
Arcs[i][j] = j;
}
}
for(k = 0; k<Vexnum; k++)
{
for(i =0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if(ArcsDis[i][j]>(ArcsDis[i][k]+ArcsDis[k][j]))
{
if((ArcsDis[i][k]!=-1)&&(ArcsDis[i][k]!=-2)&&(ArcsDis[k][j]!=-1)&&(ArcsDis[k][j]!=-2))
{
ArcsDis[i][j] = ArcsDis[i][k]+ArcsDis[k][j];
Arcs[i][j] = k;
}
}
}
}
}
for(i =0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if ((i != j)&&(ArcsDis[i][j]!=-2))
{
cout << Vex[i] << "到" << Vex[j] << "最短路径权值为:"<<ArcsDis[i][j]<<"\t路径为:";
int next = Arcs[i][j];
cout<<Vex[i];
while(next!=j)
{
cout<<"->"<<Vex[next];
next = Arcs[next][j];
}
cout<<"->"<<Vex[j]<<endl;
}
}
}
return 0;
}
int Graph::Compute(void)
{
int i,j;
for(i = 0; i<Vexnum; i++)
sum[i] = 0;
for(i = 0; i<Vexnum; i++)
{
for(j = 0; j<Vexnum; j++)
{
if((i!=j)&&(Arcs[i][j]!=-2))
sum[i] += ArcsDis[j][i];
}
cout<<"各个顶点到"<<Vex[i]<<"点的最短路径为:"<<sum[i]<<endl;
}
temp_d = sum[0];
for(i = 0; i<Vexnum; i++)
{
if(temp_d>sum[i])
{
temp_d = sum[i];
temp_l = i;
}
}
cout<<"各单位到"<<Vex[temp_l]<<"顶点的距离最短"<<temp_d<<endl;
cout<<"学校超市最佳的地址为"<<Vex[temp_l]<<endl;
return 0;
}
int Graph::print(void)
{
cout<<"Arcs"<<endl;
for(int i=0; i<Vexnum; i++)
{
for(int j=0; j<Vexnum; j++)
cout<<setw(4)<<Arcs[i][j];
cout<<endl;
}
cout<<"ArcsDis"<<endl;
for(int i=0; i<Vexnum; i++)
{
for(int j=0; j<Vexnum; j++)
cout<<setw(4)<<ArcsDis[i][j];
cout<<endl;
}
return 0;
}
int Graph::Compare(char input)
{
int i;
for(i = 0; i<Vexnum; i++)
{
if(input == Vex[i])
break;
}
return i;
}