贝尔曼福特算法

提示:功不唐捐,玉汝于成,岁月不负有心人


前言

松弛的理解
建议大家先看一下 !!!

分析

贝尔曼福特就像前面的迪杰斯特拉算法一样 ,理解起来是有难度的,但是,写出来倒不是那么难。 前面我们知道不能处理负权的一个主要原因是 迪杰斯特拉每一次进来一个新的结点 ,都是对此结点直接关联的结点,通过此结点对没有加入结果集中的结点进行松弛处理,这里贝尔曼福特的方式 每一次选取一个新的结点之后,都是对所有的边进行松弛处理,那么要经过多少次对所有边的松弛处理呢?最多不过V-1 为什么是V-1 假设极端状态下就是一个链表的形式,其中就是V-1

操作过程

知道上面这些我们就可以写出代码了,主要是这三个for
内层的两个for:j,k 表示的就是遍历所有的边 最外层的for:i 表示就是所有边的松弛的次数

	for(int i=0;i<G.vexnum-1;i++){//表示所有边松弛的次数最多n-1
		for(int j=0;j<G.vexnum;j++){ //j为源点
			for(int k=0;k<G.vexnum;k++){//k为终点
				if (D[k] > D[j] + G.Edge[j][k])
				{
					D[k] = D[j] + G.Edge[j][k];
				}
			}
		}
	}

负圈判断

再进行一次全局伸缩判断是否继续变化

for (int j = 0; j<G.vexnum - 1; j++){
		for (int k = 0; k<G.vexnum - 1; k++){
			if (dict[k] > dict[j] + G.arcs[j][k])
			{
				cout<<"存在负圈,上述路径求取不准确"<<endl;
				return; 
			}	
			
		} 
	}

代码汇总

代码78 行for(int i=2;i<G.vexnum-1;i++) 因为我们初始化过了 所有可以少两次,当然你依然使用for(int i=0;i<G.vexnum-1;i++) 也没有错误
请添加图片描述

#include<bits/stdc++.h>
using namespace std;
#define MaxInt 32767  //表示极大值∞  其实就是一种无穷标志
#define MVNum  100    //表示最大顶点数 
typedef char VerTexType;//假设顶点的数据结构类型为char 
typedef int  ArcType;//假设权值类型为整形
//下面的是邻接矩阵的 
typedef struct{
	 VerTexType vexs[MVNum];//顶点表 
	 ArcType  arcs[MVNum][MVNum];//邻接矩阵 
	 int vexnum;//图的当前顶点数
	 int arcnum;//图的当前边数
}AMGraph;
//打印邻接矩阵 
void PrintAMG(AMGraph G){
	cout<<" "<<" ";
	for(int i=0;i<G.vexnum;i++){
		cout<<G.vexs[i]<<" ";
	}
	cout<<endl;
	for(int i=0;i<G.vexnum;i++){
		cout<<G.vexs[i]<<" "; 
		for(int j=0;j<G.vexnum;j++){
			cout<<G.arcs[i][j]<<" ";
		}
		cout<<endl;
	}
} 
//创建一个无向图 ,其实就是填充两个数组 
void CreateAdjoin(AMGraph &G){
	cout<<"请输入顶点数和边数"<<endl;
	cin>>G.vexnum>>G.arcnum; 
	cout<<"请输入各个顶点名称"<<endl; 
	for(int i=0;i<G.vexnum;i++){
		cin>>G.vexs[i]; 
	}
	for(int h=0;h<G.arcnum;h++){
		char vex1;char vex2;
		cout<<"请输入弧的两个顶点"<<endl;
		cin>>vex1>>vex2; 
		//首先来看是否存在这两个顶点  若是存在则找到这两个点对应的下标
		// 当然创建的时候一种更加简单的方式就是遍历二维数组 一个一个输入
		int i=0;while(G.vexs[i++]!=vex1);
		int j=0;while(G.vexs[j++]!=vex2);
		if(i>G.vexnum||j>G.vexnum){//没有找到 
			cout<<"你输入的结点值不正确"<<endl; 
		}
		else{
			cout<<"请输入此边对应的权重"<<endl; 
			cin>>G.arcs[i-1][j-1];
			//G.arcs[j-1][i-1]=G.arcs[i-1][j-1];
		}
	}
	PrintAMG(G);
}
/**/ 
void Bellman(AMGraph &G){ 
	int dict[G.vexnum];//用来存储最短距离 
	//因为之前若是没有边 放的是0  这里将0 修改成MaxInt 
	for(int i=0;i<G.vexnum;i++){
		for(int j=0;j<G.vexnum;j++){
			if(i!=j&&0==G.arcs[i][j]){
				G.arcs[i][j]=MaxInt;
			}
		}
	}
	//初始化 
	for(int i=0;i<G.vexnum;i++){ 
		dict[i]=G.arcs[0][i];//初始化dict[] 
	} 
	//将源点加入到最优点中 
	dict[0]=0;
	cout<<"此时的dict数组是"<<endl; 
		for(int i=0;i<G.vexnum;i++){ 
		cout<<dict[i]<<" ";
		}
		cout<<endl;
	for(int i=2;i<G.vexnum-1;i++){
		for(int j=0;j<G.vexnum;j++){ // 这个for 及内层的for指的是所以的边j为源点
			for(int k=0;k<G.vexnum;k++){//k为终点
				if (dict[k] > dict[j] + G.arcs[j][k])
				{
					dict[k] = dict[j] + G.arcs[j][k];
				}
			}
		}
		cout<<"此时的dict数组是"<<endl; 
		for(int i=0;i<G.vexnum;i++){ 
		cout<<dict[i]<<" ";
		}
		cout<<endl; 
	}
	//判断是否含有负圈  再进行一波全局伸缩判断是否变化  
	for (int j = 0; j<G.vexnum - 1; j++){
		for (int k = 0; k<G.vexnum - 1; k++){
			if (dict[k] > dict[j] + G.arcs[j][k])
			{
				cout<<"存在负圈,上述路径求取不准确"<<endl;
				return; 
			}	
			
		} 
	}
	
}
int main(){
	AMGraph G;
	CreateAdjoin(G);
	Bellman(G); 
}


运行截图

使用的测试用图
请添加图片描述

请添加图片描述

注意

其实输入的时候之前使用的是邻接矩阵 存储数据,这里需要存储有向图才能进行操作(这里需要将51行注释掉 感兴趣的读者可以同样的输入 去除注释会是什么结果),因为无向图的某一个边若是负值 其实就是相当于是负圈 求得dict数组也就不准确 所以个人的愚见 ,负无向图不能使用贝尔曼福特算法!!
若是不注销效果截图
请添加图片描述

当然也可以优化 比如dict 没有变化的时候就跳出,比如设置一个pre数组什么的 这里就不写了

总结

其实写出来是不难的 主要是理解 是有一定难度的 感觉不错点个赞呗 铁子!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值