目录
概念性的东西不再赘述。
图的存储
邻接矩阵存储无向图
#include<iostream>
using namespace std;
const int N=110;
class MGraph{
private:
int vexNum,edgeNum;
char vexData[N];
int edgeData[N][N];
public:
MGraph();
void printVex();
void printEdge();
int getEdgeNum();
bool isXianglian(char ch1,char ch2);
int getDu(char ch);
};
MGraph::MGraph(){
int num1,num2;
cin>>num1;
vexNum=num1;
for(int i=0;i<num1;i++){
char ch;
cin>>ch;
vexData[i]=ch;
}
cin>>num2;
edgeNum=num2;
for(int i=0;i<num2;i++){
int from,to,w;
cin>>from>>to;
edgeData[from][to]=1;
edgeData[to][from]=1;
}
return;
}
void MGraph::printVex(){
cout<<"图的顶点有:";
for(int i=0;i<vexNum;i++){
cout<<vexData[i]<<" ";
}
cout<<endl;
return;
}
void MGraph::printEdge(){
cout<<"图的边有:";
for(int i=0;i<vexNum;i++){
for(int j=i+1;j<vexNum;j++){
if(edgeData[i][j]==1){
cout<<vexData[i]<<"-"<<vexData[j]<<" ";
}
}
}
cout<<endl;
}
int MGraph::getEdgeNum(){
int num=0;
for(int i=0;i<vexNum;i++){
for(int j=i+1;j<vexNum;j++){
if(edgeData[i][j]==1) num++;
}
}
return num;
}
bool MGraph::isXianglian(char ch1,char ch2){
int index1,index2;
for(int i=0;i<vexNum;i++){
if(vexData[i]==ch1) index1=i;
if(vexData[i]==ch2) index2=i;
}
if(edgeData[index1][index2]==1) return true;
else return false;
}
int MGraph::getDu(char ch){
int index,num=0;
for(int i=0;i<vexNum;i++){
if(vexData[i]==ch) index=i;
}
for(int i=0;i<vexNum;i++){
if(edgeData[index][i]==1) num++;
}
return num;
}
int main(){
MGraph g;
cout<<"The number of edges: "<<g.getEdgeNum()<<endl;
char ch1,ch2;
cin>>ch1>>ch2;
if(g.isXianglian(ch1,ch2)){
cout<<ch1<<" "<<"and"<<" "<<ch2<<" is connected."<<endl;
}
else{
cout<<ch1<<" "<<"and"<<" "<<ch2<<" is not connected."<<endl;
}
char ch;
cin>>ch;
cout<<ch<<" degree: "<<g.getDu(ch);
return 0;
}
浅讲几个点:
1.思路:使用二维数组(矩阵)存储图,行和列的序号可与顶点数组对应起来表示各个顶点,每一个位置的值可以表示其坐标对应的顶点的边的关系(1代表两个顶点邻接,0相反;或者直接表示每个边的权重)
2.int getEdgeNum():扫描一遍矩阵,统计
3.bool isXianglian(char ch1,char ch2):在顶点数组中找到下标(vi,vj)后,在二维数组中对应( ArcData[vi][vj] )即得是否相连
4.int getDu(char ch):找到下标vi,扫描i行(列),统计
(适用于稠密图?)
邻接表存储有向图
#include<iostream>
using namespace std;
const int N=110;
//边表结构体定义
struct ArcNode{
int adjvex;
int weight;
struct ArcNode* nextArc;
};
struct VNode{
char vertex;
struct ArcNode* firstArc;
};
class ALGraph{
private:
struct VNode adjList[N];
int vexNum,arcNum;
public:
ALGraph();
void printVex();
void printArc();
int getArcNum();
bool isXianglian(char ch1,char ch2);
void printInOutDu(char ch);
};
ALGraph::ALGraph(){
int n,m;
cin>>n;
vexNum=n;
for(int i=0;i<vexNum;i++){
char ch;
cin>>ch;
adjList[i].vertex=ch;
adjList[i].firstArc=NULL;
}
cin>>m;
arcNum=m;
for(int i=0;i<arcNum;i++){
int from,to,w;
cin>>from>>to>>w;
struct ArcNode* p=new struct ArcNode;
p->adjvex=to;
p->weight=w;
p->nextArc=adjList[from].firstArc;
adjList[from].firstArc=p;
}
return;
}
void ALGraph::printVex(){
cout<<"图的顶点有:";
for(int i=0;i<vexNum;i++){
cout<<adjList[i].vertex<<" ";
}
cout<<endl;
return;
}
void ALGraph::printArc(){
cout<<"图的边有:";
for(int i=0;i<vexNum;i++){
struct ArcNode* p=adjList[i].firstArc;
while(p!=NULL){
cout<<adjList[i].vertex<<"-"<<adjList[p->adjvex].vertex<<":["<<p->weight<<"] ";
p=p->nextArc;
}
}
cout<<endl;
return;
}
int ALGraph::getArcNum(){
int sum=0;
for(int i=0;i<vexNum;i++){
struct ArcNode* p=adjList[i].firstArc;
while(p!=NULL){
sum++;
p=p->nextArc;
}
}
return sum;
}
bool ALGraph::isXianglian(char ch1,char ch2){
int index1,index2;
for(int i=0;i<vexNum;i++){
if(adjList[i].vertex==ch1) index1=i;
if(adjList[i].vertex==ch2) index2=i;
}
struct ArcNode* p=adjList[index1].firstArc;
while(p!=NULL){
if(p->adjvex==index2){
return true;
}
p=p->nextArc;
}
return false;
}
void ALGraph::printInOutDu(char ch){
int index;
int in=0,out=0;
for(int i=0;i<vexNum;i++){
if(adjList[i].vertex==ch) index=i;
}
struct ArcNode* p=adjList[index].firstArc;
while(p!=NULL){
out++;
p=p->nextArc;
}
cout<<"out degree:"<<out<<endl;
for(int i=0;i<vexNum;i++){
if(i!=index){
p=adjList[i].firstArc;
while(p!=NULL){
if(p->adjvex==index) in++;
p=p->nextArc;
}
}
}
cout<<"in degree:"<<in<<endl;
return;
}
int main(){
ALGraph g;
cout<<"The number of edges: "<<g.getArcNum()<<endl;
char ch1,ch2;
cin>>ch1>>ch2;
if(g.isXianglian(ch1,ch2)) cout<<ch1<<" and "<<ch2<<" is connected."<<endl;
else cout<<ch1<<" and "<<ch2<<" is not connected."<<endl;
char ch;
cin>>ch;
g.printInOutDu(ch);
return 0;
}
简单汪几句:
1.思路:使用顶点表(数组形式)和边表(链表形式)存储图。
2.获得边的个数与判断是否相连都是用遍历,与邻接矩阵只是实现方式不同
3.void ALGraph::printInOutDu(char ch):获得出度直接扫描顶点位置的边表;获得入度需要扫描整个邻接表。
DFS
int visited1[N];
void Graph::DFS(int v){
cout<<VexData[v]<<" ";
visited1[v]=1;
for(int i=0;i<VexNum;i++){
if(0<ArcData[v][i]&&ArcData[v][i]<MAXX&&visited1[i]==0){
DFS(i);
}
}
return;
}
注意:
1.邻接矩阵的写法
2.思路:使用一个visited1[N]数组记录对各个顶点的访问情况。先访问一个点,在访问与它邻接的点,递归。
BFS
int visited2[N];
void Graph::BFS(int v){
queue<int> q;
q.push(v);
visited2[v]=1;
while(!q.empty()){
v=q.front();
cout<<VexData[v]<<" ";
q.pop();
for(int i=0;i<VexNum;i++){
if(0<ArcData[v][i]&&ArcData[v][i]<MAXX&&visited2[i]==0){
q.push(i);
visited2[i]=1;
}
}
}
}
注意:
1.使用了STL的队列。可以去瞅瞅使用指南。
2.这里与二叉树的层序遍历有些类似。但仍需注意:什么时候改变visited2[i]访问情况(因为我de过一个bughhh)
最小生成树
prim算法
void Graph::getMinTree1(){
for(int i=0;i<VexNum;i++){
sE[i].adjvex=0;
sE[i].lowcost=ArcData[0][i];
}
sE[0].lowcost=0;
for(int i=0;i<VexNum-1;i++){
int index=-1,min=MAXX;
for(int j=0;j<VexNum;j++){
if(sE[j].lowcost<min&&sE[j].lowcost!=0){
min=sE[j].lowcost;
index=j;
}
}
sE[index].lowcost=0;
for(int j=0;j<VexNum;j++){
if(ArcData[index][j]<sE[j].lowcost){
sE[j].lowcost=ArcData[index][j];
sE[j].adjvex=index;
}
}
}
int sum=0;
for(int i=1;i<VexNum;i++){
cout<<"("<<VexData[i]<<","<<VexData[sE[i].adjvex]<<")"<<endl;
sum+=ArcData[i][sE[i].adjvex];
}
cout<<"最小生成树的权重:"<<endl;
cout<<sum<<endl;
return;
}
之前有文章写过这两种算法,在这里水一水。嘻嘻
kruscal算法
放个链接run了
最短路径
概念:
在非网图中,最短路径是指两顶点之间经历的边数最少的路径。
在网图中,最短路径是指两顶点之间经历的边上权值之和最短的路径。
Dijkstra算法
基本思路
简言之:
从一个顶点i出发,在它与其他顶点的边找到一条最短的边,将该顶点划归于i,并再次基础上更新i到其他各顶点的距离,循环直到所有顶点都能与i相通。
基本步骤
1.遍历找到最短边,将其归顺起始顶点。
2.利用从上一步得来得信息,更新起始点到其他各边得距离
3.循环
(可以说是废话文学了)
稍微解释:
1.dist[]的数据表示起始点到各点目前的最短距离
2.path[]表示下标对应的点是从哪个点过来的。(由此我们就可以层层倒推到起始点,而这一个路径就是所记录的最短路径)
代码
int visitd3[N];
void getMinPath(int v,int dist[],int path[]){
//初始化
for(int i=0;i<vexNum;i++){
dist[i]=ArcData[v][i];
if(ArcData[v][i]==MAXX) path[i]=-1;
else path[i]=v;
}
visited3[i]=1;
//由已知情况可以得出循环的次数
for(int i=1;i<vexNum;i++){
//找到当前情况下的最短边
int index=0,Min=MAXX;
for(int j=0;j<vexNum;j++){
if(dist[j]<Min){
index=j;
Min=dist[j];
}
}
//更新
visited3[index]=1;
for(int j=0;j<vexNum;j++){
if(dist[j]>dist[index]+ArcData[index][j]){
dist[j]=dist[index]+ArcData[index][j];
path[j]=index;
}
}
}
//打印
for(int i=0;i<vexNum;i++){
cout<<dist[i]<<" ";
}
cout<<endl;
for(int i=0;i<vexNum;i++){
cout<<path[i]<<" ";
}
cout<<endl;
return;
}
当然这里没能打印出具体的路径:
我现写一下:
for(int i=0;i<VexNum;i++){
stack<char> st;
if(i!=v){
cout<<VexData[v]<<"-->"<<VexData[i]<<":";
int index=i;
st.push(VexData[index]);
while(index!=v){
index=path[index];
st.push(VexData[index]);
}
}
while(!st.empty()){
cout<<st.top()<<" ";
st.pop();
}
cout<<endl;
}
cout<<endl;
(上面那个白图是这里的测试数据)
Floyd算法
相比dijstra算法floyd真的好理解一些,且两者时间复杂度也相差无几。
基本思路
简言之:
在邻接矩阵基础上循环试探各个顶点,如果能改善路径就加入这个试探的顶点
比较重要的一点就是,各顶点到顶点的路径是动态的(感谢我的hxd老莫!!)
基本步骤
就是三重循环,加一个判断条件,具体看关键代码
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
for(int k=0;k<vexNum;k++){
if(dist[j][k]>dist[j][i]+dist[i][k]&&i!=j&&i!=k&&j!=k){
Path[j][k]=Path[j][i]+Path[i][k];
dist[j][k]=dist[j][i]+dist[i][k];
}
}
}
}
汪一下:具体路径怎么打印嘞
Dijstra人家有path数组,可以追踪回去。那我们就只好开一个二维string数组惹。(string数组对字符串的直接加减我真的吹爆)
每次要添加顶点的时候我们就直接+=就可以惹,不过稍微有一个点需要注意:
假如
A->B:ACB
B->D: B->O->D
两者如果直接相加岂不是有一个B会重复咯?
两种处理方法:
1:写一个函数,在加入的时候处理一下末尾。即:只循环加入前size()-1个各经过的顶点
void path(int x,int y,int z){
int sz=p[x][y].size();
string tp="";
for(int i=0;i<sz-1;i++)tp=tp+p[x][y][i];
p[x][z]=tp+p[y][z];
}
2.初始化加上起点,在三重循环的过程中先不加临时终点。即变成:
A->B:AC
B->D:BO
相加之后是ACBO。循环结束后,每一条路径都没有终点,但是通过邻接矩阵我们都知道终点,就再加上咯。
放俩代码
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
int t,n,m,dis[100][100];
string p[10][10];
string ch[]={"","A","B","C","D","E"};
void path(int x,int y,int z){
int sz=p[x][y].size();
string tp="";
for(int i=0;i<sz-1;i++)tp=tp+p[x][y][i];
p[x][z]=tp+p[y][z];
}
int main(){
//ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j){
dis[i][j]=0;
p[i][j]="";
}
else{
dis[i][j]=inf;
p[i][j]=ch[i]+ch[j];
}
}
}
for(int i=0,u,v,w;i<m;i++){
cin>>u>>v>>w;
dis[u][v]=dis[v][u]=w;
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(k!=i&&k!=j&&i!=j)
if(dis[i][j]>dis[i][k]+dis[k][j]){
dis[i][j]=dis[i][k]+dis[k][j];
path(i,k,j);
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
printf("%4d",dis[i][j]);
cout<<'\n';
}
cout<<'\n';
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cout<<p[i][j]<<" ";
cout<<'\n';
}
return 0;
}
#include<iostream>
#include<string>
using namespace std;
const int N=100;
const int MAXX=99999;
string Path[N][N];
int main(){
int Arc[N][N],dist[N][N];
char vexData[N];
int vexNum,ArcNum;
cin>>vexNum>>ArcNum;
for(int i=0;i<vexNum;i++){
cin>>vexData[i];
}
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
if(i==j) {
dist[i][j]=0;
Path[i][j]+=" ";
}
else{
dist[i][j]=MAXX;
Path[i][j]+=vexData[i];
}
}
}
cout<<"请输入"<<ArcNum<<"条边所连接的两个顶点和权重"<<endl;
for(int i=0;i<ArcNum;i++){
char a,b;
int w;
cin>>a>>b;
cin>>w;
int index1=0,index2=0;
for(int j=0;j<vexNum;j++){
if(a==vexData[j]) index1=j;
if(b==vexData[j]) index2=j;
}
dist[index1][index2]=w;
dist[index2][index1]=w;
}
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
for(int k=0;k<vexNum;k++){
if(dist[j][k]>dist[j][i]+dist[i][k]&&i!=j&&i!=k&&j!=k){
Path[j][k]=Path[j][i]+Path[i][k];
dist[j][k]=dist[j][i]+dist[i][k];
}
}
}
}
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
if(i!=j&&dist[i][j]<MAXX){
Path[i][j]+=vexData[j];
}
}
}
cout<<endl<<"最短路径"<<endl;
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
printf("%6d ",dist[i][j]);
}
cout<<endl;
}
cout<<endl<<"具体路径"<<endl;
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
cout<<Path[i][j]<<" ";
}
cout<<endl;
}
}
(输入略有差异)
拓扑排序
概念
老实说,原来那个太敷衍惹,看不是很懂。于是决定再稍微加一点解释。
AOV网:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,称这样的有向图为顶点表示活动的网,简称AOV网。
下面以这个图做解释,相对来说好理解一点。
以我们修课为例,课程的开设是需要合理规划的。比如,如果想要学习离散数学,就需要先学习高等数学,想要学习计算机原理,就要先有计算机导论和程序设计的基础。(内含一个充分必要关系在里面),由此我们可以知道每一个课程都是有先后顺序的,即优先关系。一项工程也可以类比于我们修课。引入一个表示工程的有向网---AOV网。他的顶点表示活动(课程),弧表示优先关系(先决条件)。
讲完了AOV,我大抵是可以讲拓扑排序了。
拓扑序列:设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1, v2, …, vn称为一个拓扑序列,当且仅当满足下列条件:若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在顶点vj之前。
拓扑排序:对一个有向图构造拓扑序列的过程称为拓扑排序 。
拓扑序列使得AOV网中所有应存在的前驱和后继关系都能得到满足。
概念看看就好。
简单说来,拓扑序列就是弧的两端的顶点出现的顺序一定要满足,弧尾顶点(没有箭头的一方)在弧头顶点(有箭头的一方)的前面。
ps:A---->B,A是弧尾,B是弧头。
构造拓扑序列的过程就叫拓扑排序。
注意:
1.AOV网中不能出现回路(这样就不能完成任务啦~)
2.这是有向图
思路
1.找到AOV网中没有入度的点,并将其输出。
2.在网中删除该点,并删除以该点为尾的弧
3.循环,直到全部顶点被输出。
处理操作
(以临界矩阵为例)
1.准备一个统计入度的数组in[N],遍历初始化该数组。(咦,这个统计入度好像写过)
2.找到入读为0的点,输出、记录下标index并标记为已访问。
3.遍历以index为起始的边,如果存在,就把结尾的顶点的in--
比较
1° 将所有以index为起始的边全部删除(赋0),再更新in
2° 直接把结尾的顶点的in--
(更新变边的关系好像不是很必要)
代码
(这个代码我偷个懒,直接输入邻接矩阵)
#include<iostream>
using namespace std;
const int N=110;
char vexData[N];
int vexNum,arcNum;
int arcData[N][N];
int in[N];
int main(){
int n;
cin>>n;
vexNum=n;
for(int i=0;i<n;i++){
cin>>vexData[i];
}
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
cin>>arcData[i][j];
}
}
cout<<"拓扑排序:"<<endl;
//初始化
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
in[i]+=arcData[j][i];
}
}
while(n--){
int index,i=0;
while(in[i]!=0) i++;
index=i;
if(index==vexNum){
cout<<"存在回路"<<endl;
return 0;
}
cout<<vexData[index]<<" ";
in[index]=-1;
for(i=0;i<vexNum;i++){
if(arcData[index][i]==1&&in[i]>0) in[i]--;
}
}
}
代码应该好懂吧。
关键路径
概念
AOE网:在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间,称这样的有向图叫做边表示活动的网,简称AOE网。AOE网中没有入边的顶点称为始点(或源点),没有出边的顶点称为终点(或汇点)。
AOE网的性质:
⑴ 只有在某顶点所代表的事件发生后,从该顶点出发的各活动才能开始;
⑵ 只有在进入某顶点的各活动都结束,该顶点所代表的事件才能发生。
依旧举一个例子。
这里以生产一辆汽车为例,开始这个事件可以直接发生,而后,我们需要生产外壳、发动机等一系列零部件,每一项活动都要消耗一定时间。当完成某一项活动时,会达成一项事件,比如生产外壳这个活动完成后,我们就完成了外壳加工(不要觉得是废话!我是想区分一下这里的活动跟事件)。当这些活动都完成之后(事件都达到时候),我们才能所有零部件集中起来,当然这个集中的过程也是一项活动,完成后可达到一个事件。
(www,不知道我是否讲清楚了)
相信也看出来了,活动是一个过程,而事件是经历了一个过程之后达到的一种状态(我的理解)。
再来看AOE网的概念和性质应该会好理解一些了。
关键路径:在AOE网中,从始点到终点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。
关键活动:关键路径上的活动称为关键活动。
类似于短板效应,装水的容量取决于你最短的那块板,而这里花费时间最多的路径就是那一个最短的板。
思路
理清几个点:
事件的最早发生时间:指从始点开始到顶点vk的最大路径长度
已经说过一个事件要触发需要满足到他的每一个活动都完成,所以最早发生事件就是最大路径长度咯,(从前往后)
事件的最晚发生时间:指在不推迟整个工期的前提下,事件vk允许的最晚发生时间。
这个从这个概念就可以看出是从后往前了。并且基于此,我们可以很容易地想到,最早发生时间与最晚发生的时间之间有一段间隙可以摸一会儿鱼,有一定的弹性。那么他就必定不是关键路径。
活动的最早发生时间:就是离他最近的触发事件的事件的最早发生时间
活动ai的最早发生时间就是事件vk的 最早发生时间
活动的最晚发生时间:保证他后面的事件的最迟发生时间不拖后
我个人感觉需要好好理解一下。(也不是很复杂)
思路
在概念都搞清楚了之后,我们就可以得到关键路径所满足的的条件了。即,最早发生时间==最晚发生时间,(没有空隙摸鱼)。
从起始点A开始,求到每一个点的最大值。即为ve
从终点B出发,反向求到每一个点的最小值。即为vl
而e和l就更简单了
e[i]=ve[active[i].from];
l[i]=vl[active[i].to]-active[i].w;
代码
#include<iostream>
using namespace std;
const int N=110;
const int MAXX=99999;
char vexData[N];
int vexNum,arcNum;
int arcData[N][N];
int ve[N],vl[N];
int e[N],l[N];
struct Node{
int from;
int to;
int w;
};
//隐含条件:已经拓扑排序
int main(){
int n,m;
struct Node active[N];
cin>>n>>m;
vexNum=n;
arcNum=m;
for(int i=0;i<n;i++){
cin>>vexData[i];
}
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
if(i==j) arcData[i][j]=0;
else arcData[i][j]=MAXX;
}
}
for(int i=0;i<arcNum;i++){
int a,b,w;
cin>>a>>b>>w;
arcData[a][b]=w;
active[i].from=a;
active[i].to=b;
active[i].w=w;
}
for(int i=0;i<vexNum;i++){
for(int j=0;j<vexNum;j++){
cout<<arcData[i][j]<<" ";
}
cout<<endl;
}
cout<<"关键路径之ve数组"<<endl;
ve[0]=0;
for(int k=1;k<vexNum;k++){
ve[k]=0;
for(int i=0;i<vexNum;i++){
if(arcData[i][k]!=0&&arcData[i][k]!=MAXX){
if(ve[i]+arcData[i][k]>ve[k]) ve[k]=ve[i]+arcData[i][k];
}
}
}
for(int i=0;i<vexNum;i++) cout<<ve[i]<<" ";
cout<<endl;
cout<<"关键路径之vl数组"<<endl;
vl[vexNum-1]=ve[vexNum-1];
for(int k=vexNum-2;k>=0;k--){
vl[k]=MAXX;
for(int i=0;i<vexNum;i++){
if(arcData[k][i]!=0&&arcData[k][i]!=MAXX){
if(vl[i]-arcData[k][i]<vl[k]) vl[k]=vl[i]-arcData[k][i];
}
}
}
for(int i=0;i<vexNum;i++) cout<<vl[i]<<" ";
cout<<endl;
for(int i=0;i<arcNum;i++){
e[i]=ve[active[i].from];
l[i]=vl[active[i].to]-active[i].w;
}
cout<<endl<<"e数组"<<endl;
for(int i=0;i<arcNum;i++) cout<<e[i]<<" ";
cout<<endl<<"l数组"<<endl;
for(int i=0;i<arcNum;i++) cout<<l[i]<<" ";
return 0;
}
实不相瞒,我有一点没说清楚,后续在补一补吧
补:
1.这里的关键路径已经默认是在拓扑排序已经排好序的基础上了,如果后续碰到了一般还是自己拍一下咯。
做题时候的算法模板
单源&&正权&&稠密图的最短路---->Dij朴素
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N=510;
const int INF=0x3f3f3f;
int dist[N],g[N][N];
bool st[N];
int n,m;
int dij(){
memset(dist,INF,sizeof(dist));
dist[1]=0;
for(int i=0;i<n;i++){
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j])) t=j;
if(t==n) break;
st[j]=true;
for(int j=1;j<=n;j++){
dist[j]=min(dist[t]+g[t][j]);
}
}
return dist[n];
}
int main(){
cin>>n>>m;
memset(g,INF,sizeof(g));
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
}
int t=dij();
cout<<t;
return 0;
}
1.st数组表示一个顶点是否已经确定最短路
单源&&正权&&稀疏图的最短路---->Dij优化
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=510;
const int INF=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int dij(){
memset(dist,INF,sizeof(dist));
dist[1]=0;
for(int i=0;i<n;i++){
int t=-1;
for(int j=1;j<=n;j++){
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
}
st[t]=true;
for(int j=1;j<=n;j++){
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
if(dist[n]==INF) return -1;
else return dist[n];
}
int main(){
cin>>n>>m;
memset(g,INF,sizeof(g));
while(m--){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c); //重边
}
int t =dij();
cout<<t<<endl;
return 0;
}