优质博客、参考出处
最短路几个算法
floyd 单源、有向、负权 O(N^3)
dijkstra 单源、无负权 O(VV+E) 【利用优先队列优化,邻接矩阵储存O(ElogE)】
bellmen_ford 单源、判断有无负权 O(VE)
spfa 单源、负权 O(k*E) k<<V,k远远小于V
板题hdu2544
复习的过程中出错的点:
1、map数组初始化,二维数组初始化的时候用memset初始化无效
要用二层循环初始化。
2、注意标记好v后在后面遍历是v为中间点,不是i
//
// main.cpp
// 最段路_模版_m
//
// Created by 陈冉飞 on 2019/8/16.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include <iostream>
using namespace std;
#include <cstring>
#define cl(a,b) memset(a,b,sizeof(a))
#define maxn 105
int map[maxn][maxn],vis[maxn],d[maxn];
#define INF 1<<29
int n,m,i,j,a,b,c;
void dijkstra(){
for (i = 1; i <= n; i++) d[i] = map[1][i];
// vis[1] = 1;d[1] = 0;
int v,tem;
for (i = 1; i <= n; i++){
tem = INF;
for (j = 1; j <= n; j++)
if (!vis[j] && d[j] <= tem) {v = j;tem = d[j];}
vis[v] = 1;
for (j = 1; j <= n; j++)
if (!vis[j] && d[j] > d[v] + map[v][j]) d[j] = map[v][j] +d[v];
}
cout<<d[n]<<endl;
}
int main(int argc, const char * argv[]) {
while (~scanf("%d%d",&n,&m) && n+m) {
cl(vis, 0);cl(d, 0);
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (i == j) map[i][j] = 0;
else map[i][j] = INF;
for (i = 1; i <= m; i++) {
scanf("%d%d%d",&a,&b,&c);
map[a][b] = c;map[b][a] = c;
}
// for (i = 1; i <= n; i++){
// for (j = 1; j <= n; j++)
// cout<<map[i][j]<<" ";
// cout<<endl;}
dijkstra();
}
return 0;
}
模版中的模版,经典中的经典----单源无负权求最段路
dijkstra算法
利用一个vis来标记是否该路径的最值是否为最小。
//
// main.cpp
// 最段路_模版题
//
// Created by 陈冉飞 on 2019/7/23.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include<iostream>
#include<vector>
using namespace std;
#define maxn 1050
#define inf 1<<29
int map[maxn][maxn];
int point_total,dis_total;
void dijkstra(){
int i,j;
bool is_vis[maxn]; //1 到索引处的距离是否确定
int d[maxn];//开一个储存到这个点距离的数组
for(i = 1;i <= point_total;i++){ //初始化
is_vis[i] = 0;
d[i] = map[1][i];
}
int v,min ;
//选取每次的最小值时应该在每次里更新最小值,防止影响后续的遍历
for(i = 1; i <= point_total;i++){
min = inf;
for(j = 1;j <= point_total;j++){
if(!is_vis[j] && d[j] < min){ //如果没有被访问过且中间有联系
//确定到j的距离,并
v = j;
min = d[j];
}
}
//从一开始遍历一遍后发现一个到的距离最小的
is_vis[v] = 1;
//然后再重新遍历刷新最小值
for(j = 1;j <= point_total;j++){
if(!is_vis[j]&&d[j] > map[v][j]+d[v]){ //看直接到大还是中间通过v中转一下距离小
d[j] = map[v][j] + d[v];
}
}
}
cout<<d[point_total]<<endl;
}
int main(){
while (~scanf("%d%d",&dis_total,&point_total)) {
//首先初始化所以的距离,没有值的赋成正无穷 从1开始赋值
for(int i = 1;i <= point_total;i++){
for(int j = 1;j <= point_total;j++){
if(i == j){ //在自己的点
map[i][j] = 0;
}else{
map[i][j] = inf;
map[j][i] = inf;
}
}
}
int tema ,temb,teml;
for(int i = 0;i < dis_total;i++){
scanf("%d%d%d",&tema,&temb,&teml);
if (teml<map[tema][temb]) {
map[tema][temb] = teml;
map[temb][tema] = teml;
}
}
dijkstra();
}
return 0;
}
bellmen_ford算法:直接遍历刷新最小值
//
// main.cpp
// 最段路_模板题_bf
//
// Created by 陈冉飞 on 2019/7/23.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include <iostream>
using namespace std;
#define maxn 2005
//bf 用到了结构体储存节点之间的关系
struct dis {
int a,b,l;
}all_dis[maxn];
int point_total,dis_total;
void bellmen_ford(){
int d[maxn];
//首先初始化所有的距离
d[1] = 0;
for (int i = 2; i <= point_total; i++) {
d[i] = 0x3ffffff;
}
for (int i = 0; i < point_total; i++) {
for (int j = 0; j < dis_total; j++) {
//a到原。 b到原+a到b
if (d[all_dis[j].a] > d[all_dis[j].b]+all_dis[j].l) {
d[all_dis[j].a] = d[all_dis[j].b]+all_dis[j].l;
}
if (d[all_dis[j].b] > d[all_dis[j].a]+all_dis[j].l) {
d[all_dis[j].b] = d[all_dis[j].a]+all_dis[j].l;
}
}
}
for (int i = 0; i < point_total; i++) {
cout<<d[i]<<endl;
}
cout<<d[point_total]<<endl;
}
int main(int argc, const char * argv[]) {
int tema,temb,teml;
while(~scanf("%d%d",&dis_total,&point_total)){
for (int i = 0; i < dis_total; i++) {
scanf("%d%d%d",&tema,&temb,&teml);
all_dis[i].a = tema;
all_dis[i].b = temb;
all_dis[i].l = teml;
}
bellmen_ford();
}
return 0;
}
spfa算法 类似bfs 利用一个队列维护这个起点开始弥散式的距离,通过每次将这个点相邻的push进队列再取出,然后来维护各个路径的最小值。
(不一样的事spfa中和这个点相邻的点可以被多次push进队列,来不断维护最小距离)
//
// main.cpp
// 最段路_模版_spfa
//
// Created by 陈冉飞 on 2019/7/31.
// Copyright © 2019 陈冉飞. All rights reserved.
//
#include <iostream>
#include <queue>
using namespace std;
int h[1005];
struct dis{
int a,b,l,next; //next 是用来在每个路径指向下一个的方式,在输入的时候只要是输入的有路径的那个都会让他有next指示
}all_dis[4005];
int point_total,dis_total;
void spfa(){
int d[1005],vis[1005];
for (int i = 2; i <= point_total; i++) {
d[i] = 0x3ffffff;
vis[i] = 0;
}
d[1] = 0;
vis[1] = 1;
queue<int>q;
//把起点1拿出来,然后开始遍历
q.push(1);
int pos;
while (!q.empty()) {
// cout<<"one"<<endl;
//拿出队首的元素
pos = q.front();q.pop();
vis[pos] = 0;
//从这个起点开始遍历 第一个遍历完了之后引向下一个
for (int i = h[pos]; i != -1; i = all_dis[i].next) {
// cout<<d[all_dis[i].b]<<endl;
// cout<<"two"<<endl;
//开始刷新d[输入的这个点到相邻的b]的距离
if (d[pos]+all_dis[i].l < d[all_dis[i].b]) {
//刷新了最小值
d[all_dis[i].b] = d[pos]+all_dis[i].l;
//然后往后铺路 这个点后边的路没有被发现,则添加这个路
if(vis[all_dis[i].b] == 0){
vis[all_dis[i].b] = 1;
q.push(all_dis[i].b);
}
}
}
// for (int i = 1; i <= point_total; i++) {
// cout<<i<<" "<<d[i]<<endl;
// }
}
cout<<d[point_total]<<endl;
}
int main(int argc, const char * argv[]) {
while (~scanf("%d%d",&dis_total,&point_total)) {
for (int i = 1; i <= point_total; i++) {
h[i] = -1;
}
int tema,temb,teml;
for (int i = 1; i <= 2*dis_total ; i+=2) { //此处赋值的i是对应的
scanf("%d%d%d",&tema,&temb,&teml);
//第一组
all_dis[i].a = tema;
all_dis[i].b = temb;
all_dis[i].l = teml;
all_dis[i].next = h[tema];
//再指回来
h[tema] = i;
//第二组
all_dis[i+1].a = temb;
all_dis[i+1].b = tema;
all_dis[i+1].l = teml;
all_dis[i+1].next = h[temb];
h[temb] = i+1;
}
spfa();
}
return 0;
}