Dijkstra算法解释在下面,点目录可以直接跳转。
先提供三种算法的基础模板:
- 1.Floyd
//floyd
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1007;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int dis[N][N];
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
memset(dis,0x3f,sizeof(dis));
for(int i=0,u,v,w;i<m;++i){
scanf("%d%d%d",&u,&v,&w);
dis[u][v]=min(dis[u][v],w);
}
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
for(int i=2;i<=n;++i){
printf("1点到%d点的最短路径:%d\n",i,dis[1][i] );
}
}
return 0;
}
/*
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
*/
- 2.dijkstra:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<assert.h>
#include<bitset>
#define lson rt<<1
#define rson rt<<1|1
#define lsonl l,mid,rt<<1
#define rsonr mid+1,r,rt<<1|1
#define lowbit(x) (x)&(-(x))
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int INF = 0x3f3f3f3f;
const int N = (int)1e5 +107;
struct lp{
int v, w;
lp(){}
lp(int a,int b){v=a;w=b;}
friend bool operator <(const lp &a,const lp &b){
return a.w>b.w;
}
}cw[N];
int n, m;
int dis[N], vis[N];
vector<pii> mp[N];
void dij(){
priority_queue<lp> Q;
Q.push(lp(n,0));
dis[n]=0;
while(!Q.empty()){
lp b = Q.top();Q.pop();
if(vis[b.v])continue;
vis[b.v]=1;
int len = mp[b.v].size();
for(int i=0;i<len;++i){
int v = mp[b.v][i].first,w=mp[b.v][i].second;
if(vis[v])continue;
if(dis[v]>dis[b.v]+w){
dis[v]=dis[b.v]+w;
Q.push(lp(v,dis[v]));
}
}
}
printf("%d\n", dis[1]);
}
inline void init(){
for(int i=1;i<=n;++i)mp[i].clear();
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
}
int main(){
while(~scanf("%d%d", &m, &n)){
init();
for(int i=0,u,v,w;i<m;++i){
scanf("%d%d%d", &u, &v, &w);
mp[u].push_back(make_pair(v,w));
mp[v].push_back(make_pair(u,w));
}
dij();
}
return 0;
}
- 3.spfa:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1007;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int tot, dist[N], head[N],vis[N];
int n, m, w;
struct Edge {
int v, w, nex;
} cw[N];
void add(int u, int v, int w){
cw[++tot].v=v;cw[tot].w=w;cw[tot].nex=head[u];head[u]=tot;
}
void spfa(){
queue<int>Q;
while(!Q.empty())Q.pop();
memset(dist,0x3f,sizeof(dist));
memset(vis,0,sizeof(vis));
dist[1]=0;
Q.push(1);
while(!Q.empty()){
int p=Q.front();Q.pop();
vis[p]=0;
for(int i=head[p];i!=-1;i=cw[i].nex){
int q=cw[i].v;
if(dist[q]>dist[p]+cw[i].w){
dist[q]=dist[p]+cw[i].w;
if(!vis[q]){
vis[q]=1;
Q.push(q);
}
}
}
}
}
int main(){
int a, b, c;
while(~scanf("%d%d", &n, &m)) {
tot = -1;
memset(head, -1, sizeof(head));
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
spfa();
for(int i=2;i<=n;++i){
printf("1点到%d点的最短路径:%d\n",i,dist[i] );
}
}
return 0;
}
迪杰斯特拉算法:
算法讲解:
dijkstra是用来求单源最短路径的。算法结束后dis[i]保存的是起点 s 到 i 点的最短路径。下面简单讲一下算法过程。
- vis数组记录该点是否松弛过,可以理解为是否访问过。初始vis[1]=1,其他为0。可以证明的是算法最多松弛n-1次。反正可以证明。
- ar[][]用来存图。初始化为INF,如果a和b有一条权值为c的有向边,则ar[a][b]=c;;如果是无向边就是ar[a][b]=ar[b][a]=c;
- dis数组用来松弛,并且存最后起点到各个点的最短距离。
- 松弛一条边(u,v)的意思是能否从u点找到一个中介点p使得ar[u][p]+ar[p][v] < ar[u][v]。松弛完后,得到u到v的一条更短的路径。将图中除起点外的n-1个点松弛后,就可以得出起点到每个店的最短路了。
1.以1为起点做栗子,初始时点1只和点2和3相连,所以dis[1]=0;dis[2]=2;dis[3]=12;其他赋值为INF无穷大。
2.第二步类似与讲解prim算法的那一步,找出离起点最近的一个没有被访问过的点p。第一步的时候距离最近的是dis[2]=2。所以p=2;p点将被用来松弛,还有记得标记vis[p]=1;
3. 得到p点后遍历每个顶点,看顶点 i 是否通过这个中介点p 能 得出一条从起点s到点 i 的更短距离。比如说:第一轮的时候vis[3]还是0,且dis[3]=12;dis[p]+ar[p][3]=5;5小于12,非常棒所以dis[3]更新为5,还有vis[4]=0,且dis[4]=INF,dis[p]+ar[p][4]=8;8 < INF,所以dis[4]更新为8。然后vis[5]=vis[6]=0,dis[5]=dis[6]=INF,dis[p]+ar[p][5]=dis[p]+ar[p][6]=INF+2。这两个点就不用更新。
4. 重复第二步,共最多进行n-1次。因为n-1次后每个点应该都被标记了。前提是图联通,图不连通另说。
就这样讲完了,这时候再去上面看看上面的dij的代码,应该就感觉很好理解了。
下面附上一道例题:
题目链接:hdu1548
AC代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N=205;
const int INF=0x3f3f3f3f;
int n;
int ar[N][N],k[N],dis[N];
bool vis[N];
void dijkstra(int x,int y){
for(int i=1;i<=n;i++){//初始化
dis[i]=ar[x][i];//dis[i]保存的是起点到i点的最短距离
vis[i]=0;
}
dis[x]=0;
vis[x]=1;
for(int i=1;i<n;i++){
int mi=INF,p=0;
for(int j=1;j<=n;j++){
if(mi>dis[j]&&!vis[j]){
mi=dis[j];
p=j;
}
}
if(mi==INF)break;
vis[p]=1;
for(int j=1;j<=n;++j){
if(!vis[j]){
dis[j]=min(dis[j],dis[p]+ar[p][j]);
}
}
}
}
int main(){
while(~scanf("%d",&n)&&n){
int s,e;
scanf("%d%d",&s,&e);
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
ar[i][j]=INF;
}
}
for(int i=1;i<=n;i++){
scanf("%d",&k[i]);
if(i+k[i]<=n){
ar[i][i+k[i]]=1;
}
if(i-k[i]>=1){
ar[i][i-k[i]]=1;
}
}
dijkstra(s,e);
printf("%d\n",dis[e]==INF?-1:dis[e]);
}
return 0;
}
优先队列优化后的dijkstra算法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<string>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 1010;
const int INF = 0x3f3f3f3f;
const LL mod = 10000;
const double eps = 1e-8;
struct lp{
int to,w;
lp(int a,int b){to=a;w=b;}
bool operator <(const lp &a)const {
if(w!=a.w) return w>a.w;
return to<a.to;
}
};
vector<lp>mp[N];
int dis[N],vis[N],s[N],d[N];
int tot,t,S,D;
void dij(int s){
priority_queue<lp>Q;
Q.push(lp(s,0));
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
while(!Q.empty()){
lp x=Q.top();Q.pop();
for(int i=0;i<mp[x.to].size();++i){
lp t=mp[x.to][i];
if(dis[t.to]>x.w+t.w){
dis[t.to]=x.w+t.w;
Q.push(lp(t.to,dis[t.to]));
}
}
}
}
int main(){
while(~scanf("%d%d%d",&t,&S,&D)){
for(int i=0;i<N;++i)mp[i].clear();
int a,b,c;
for(int i=0;i<t;++i){
scanf("%d%d%d",&a,&b,&c);
mp[a].push_back(lp(b,c));
mp[b].push_back(lp(a,c));
}
for(int i=0;i<S;++i){
scanf("%d",&s[i]);
}
for(int i=0;i<D;++i){
scanf("%d",&d[i]);
}
int mmin=INF;
for(int i=0;i<S;++i){
dij(s[i]);
for(int j=0;j<D;++j){
mmin=min(mmin,dis[d[j]]);
}
}
printf("%d\n",mmin);
}
return 0;
}
spfa判断负环:
POJ 3259
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<string>
#include<cmath>
#define test printf("***\n")
#define ka getchar();getchar()
#define ka1 getchar()
#define iis std::ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
const int N = 5210;
const int INF = 0x3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
int tot, dist[N], head[N],vis[N];
int num[N];
int n, m, w;
struct Edge {
int v, w, nex;
} cw[N];
void add(int u, int v, int w){
cw[++tot].v=v;cw[tot].w=w;cw[tot].nex=head[u];head[u]=tot;
}
bool spfa(){
queue<int>Q;
while(!Q.empty())Q.pop();
memset(dist,0x3f,sizeof(dist));
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
dist[1]=0;
num[1]++;//如果一个点进入队列的次数大于n次则存在负环
Q.push(1);
while(!Q.empty()){
int p=Q.front();Q.pop();
vis[p]=0;
for(int i=head[p];i!=-1;i=cw[i].nex){
int q=cw[i].v;
if(dist[q]>dist[p]+cw[i].w){
dist[q]=dist[p]+cw[i].w;
if(!vis[q]){
vis[q]=1;
num[q]++;
Q.push(q);
if(num[q]>=n){
return true;
}
}
}
}
}
return false;
}
int main() {
int T, a, b, c;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &w);
tot = 1;
memset(head, -1, sizeof(head));
for(int i = 1; i <= m; i++) {
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
for(int i=1;i<=w;++i){
scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
if(spfa() )printf("YES\n");
else printf("NO\n");
}
return 0;
}
本题bellmanFord写法
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5210;
const int INF = 0x3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
int dist[N],head[N],tot;
int n,m,w;
struct lp{
int u,v,w,nex;
}cw[N];
void add(int x,int y,int z){
cw[++tot].v=y;cw[tot].u=x;cw[tot].w=z;cw[tot].nex=head[x];head[x]=tot;
}
bool bellmanFord(){
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
for(int i=0;i<n;++i){
for(int j=2;j<=tot;++j){
int v=cw[j].v,u=cw[j].u,w=cw[j].w;
if(dist[v]>dist[u]+w){
dist[v]=dist[u]+w;
}
}
}
for(int i=2;i<=tot;++i){
if(dist[cw[i].v]>dist[cw[i].u]+cw[i].w){
return 1;
}
}
return 0;
}
int main(){
int t,a,b,c;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&w);
tot=1;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;++i){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=w;++i){
scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
if(bellmanFord() )printf("YES\n");
else printf("NO\n");
}
return 0;
}
JAVA实现
例题:传送门
70分代码:
import java.beans.IntrospectionException;
import java.io.*;
import java.lang.reflect.Array;
import java.math.*;
import java.util.*;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.text.DecimalFormat;
import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main{
static PrintStream p = System.out;
static int mod = (int)1e9+7;
static int N = 10005;
static long INF = (long)1e17;
class lp{
public int u,v,nex;
public long w;
lp(int a,int b,long c,int d){
this.u=a;this.v=b;this.w=c;this.nex=d;
}
}
public int tot;
public lp[] cw;
public int[] head;
public int[] vis;
public long[] dis;
public boolean spfa(int n){
Queue<Integer>Q = new PriorityQueue<Integer>();
int[] num = new int[n+1];
Arrays.fill(num,0);
Arrays.fill(vis,0);
for(int i=0;i<=n;++i)dis[i]=INF;
dis[1]=0;
num[1]++;
Q.add(1);
while (!Q.isEmpty()){
Integer u = Q.poll();
vis[u]=0;
for(int i=head[u];i!=-1;i=cw[i].nex){
int v=cw[i].v;
long w=cw[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(vis[v]==1)continue;
num[v]++;
vis[v]=1;
Q.add(v);
if(num[v]>n)return false;
}
}
}
for(int i=2;i<=n;++i){
p.println(dis[i]);
}
return true;
}
public void add(int x,int y,long z){
cw[++tot]=new lp(x,y,z,head[x]);
head[x]=tot;
/*cw[++tot]=new lp(y,x,z,head[y]);
head[y]=tot;*/
}
public static void main(String []args){
Scanner cin = new Scanner(System.in);
Main mai = new Main();;
while (cin.hasNext()) {
int n = cin.nextInt();
int m = cin.nextInt();
mai.cw = new lp[m+10];
mai.head = new int[n+1];
mai.dis = new long[n+1];
mai.vis = new int[n+1];
Arrays.fill(mai.head,-1);
mai.tot = 1;
for (int i = 0; i < m; ++i) {
int x = cin.nextInt();
int y = cin.nextInt();
long z = cin.nextLong();
mai.add(x, y,z);
}
boolean flag = mai.spfa(n);
}
}
}