【图基础】

目录

图的存储

邻接矩阵存储无向图

邻接表存储有向图

DFS

BFS

最小生成树

prim算法

kruscal算法

最短路径

Dijkstra算法

基本思路

基本步骤

代码

Floyd算法

基本思路

基本步骤

 汪一下:具体路径怎么打印嘞

拓扑排序

概念

 思路

处理操作

代码

关键路径

概念

思路

思路

代码


概念性的东西不再赘述。

图的存储

邻接矩阵存储无向图

#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算法

http://t.csdn.cn/2CgMm

放个链接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=(VE)是一个具有n个顶点的有向图,V中的顶点序列v1, v2, …, vn称为一个拓扑序列,当且仅当满足下列条件:若从顶点vivj有一条路径,则在顶点序列中顶点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;	 
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值