图论_三类最短路径模板

1.floyd

// 
/*
 * f[i][j]表示i到j的最短路径 
 * sum[i][j]表示i到j的最短路径个数  
 * 图的输入格式为邻接矩阵 
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 405;
int n, m;
int f[MAXN][MAXN];
int sum[MAXN][MAXN];
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++){
			scanf("%d", &f[i][j]);
			if(f[i][j]) sum[i][j] = 1;
			if(!f[i][j] && i != j) f[i][j] = 1e9 + 7;
		}
	}
		
	for(int k = 1; k <= n; k++)
		for(int i = 1; i <= n; i++) 
			for(int j = 1; j <= n; j++)
				if(f[i][j] > f[i][k] + f[k][j])
					f[i][j] = f[i][k] + f[k][j],  sum[i][j] = sum[i][k] * sum[k][j];
				else if(f[i][j] == f[i][k] + f[k][j]) sum[i][j] += sum[i][k] * sum[k][j];
	
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) printf("%d ", f[i][j]);
		printf("\n");
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) printf("%d ", sum[i][j]);
		printf("\n");
	}
	return 0;
}

2.dijkstra

#include<iostream>
#include<iomanip>//控制格式
#include<string>


#define INF 0x3f3f3f3f//定义无穷大
using namespace std;


#define vertexNum 5//源点数
int G[vertexNum][vertexNum];//邻接矩阵
string vertex[]={"A","B","C","D","E"};//源点字符串组
void CreateMGraph()
{
    for(int i=0;i<vertexNum;i++)
        for(int j=0;j<vertexNum;j++)
    {
        G[i][j]=INF;
    }
    G[0][1]=10;G[0][3]=30;G[0][4]=100;
    G[1][2]=50;
    G[2][4]=10;
    G[3][2]=20;G[3][4]=60;
}
void Dijkstra(int v)
{
    int w[vertexNum];// w为权值存储数组
    int i, num, k, min;
    string path[vertexNum];
    int s[vertexNum]={0};// 初始化标记数组
    cout<<"初始权值数组和路径字符串数组:"<<endl;
    for(i=0;i<vertexNum;i++)
    {
        w[i] = G[v][i];
        path[i]=vertex[v]+"-->"+vertex[i];
        cout<<path[i]<<" ";
        if(w[i] != INF) cout << w[i] << endl;
        else cout<<"∞"<<endl;
    }
    s[v] = 1; // 标记第一个访问点
    w[v] = 0;
    num = 1; // 计数器
    cout << "打印源点A到各点的最短路径及其权值和:" << endl;
    while(num <= vertexNum) // 当不取等号时将不会获得A到自身的路径
    {
        min=INF;
        for(k=0,i=0; i<vertexNum; i++) // 查找最小值
        {
            if(s[i]==0 && (w[i]<min)) 
            {
            	min = w[i];
            	k=i;
            }
        }
        cout << path[k] << "  " << w[k] << endl; // 打印路径及对应路径长(权值和)
        s[k] = 1;
        num++;
        for(i=0;i<vertexNum;i++) // 更新权值数组和字符串数组
        {   
        	if((w[i] > w[k]+G[k][i]) && (G[k][i]!=INF))
       	 	{
            	w[i] = w[k]+G[k][i];
            	path[i] = path[k] + "-->" + vertex[i];
            }
        }
    }
}


int main()
{
    CreateMGraph(); // 创建邻接矩阵
    cout << "打印邻接矩阵:" << endl;
     for (int i=0; i < vertexNum; i++) {
        for (int j=0; j < vertexNum; j++) {
        	if(G[i][j] == INF) cout << setw(4) << "∞";
        	else cout << setw(4) << G[i][j];
        	if(j == vertexNum-1) cout << endl;
    	}
	}
    Dijkstra(0); // 以A为源点
    return 0;
}
// 优化版
/*
 * dijkstra 单原最短路算法 
 * 邻接矩阵存图 
*/
#include <iostream>
#include <cstring>
#include <climits>
#include <queue>
#include <cstdio>

#define fi first 
#define se second 
#define REP(I, A, B) for(int I = A; I <= B; I++)
using namespace std;
const int MAXN = 1e4 + 5;
const int MAXM = 5e5 + 5;
const int INF = INT_MAX;
int d[MAXN], head[MAXN];
int nxt[MAXM], val[MAXM], to[MAXM], size;
int n, m, s;
typedef pair<int,int> pii;
priority_queue <pii, vector<pii>, greater<pii> > q;

inline void AddEdge(int u, int v, int w) {
	nxt[++size] = head[u];
	to[head[u] = size] = v;
	val[size] = w;
}

inline int read() {
	int x = 0; char ch = getchar();
	while(!isdigit(ch)) ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
	return x;
}

int main() {
	n = read(), m = read(); s = read();
	REP(i, 1, m) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c);
	}
	REP(i, 1, n) d[i] = INF;
	q.push(pii(d[s] = 0, s));
	REP(i, 1, n) { // 优化 
		while(d[q.top().se] != q.top().fi) q.pop(); // 优化 
		pii now = q.top();
		q.pop();
		for(int e = head[now.se]; e; e = nxt[e]) {
			if(d[to[e]] > val[e] + now.fi) {
			 	d[to[e]] = val[e] + now.fi;
			 	q.push(pii(d[to[e]], to[e]));
			 }
		}
	}
	REP(i, 1, n) printf("%d%c", d[i], " \n"[i == n]);
	return 0;
}

// 大学经典教材<<数据结构>>(C语言版 严蔚敏 吴为民 编著) 中该算法的实现 
/*
测试数据 教科书 P189 G6 的邻接矩阵 其中 数字 1000000 代表无穷大
6
1000000 1000000 10 100000 30 100
1000000 1000000 5 1000000 1000000 1000000
1000000 1000000 1000000 50 1000000 1000000
1000000 1000000 1000000 1000000 1000000 10
1000000 1000000 1000000 20 1000000 60
1000000 1000000 1000000 1000000 1000000 1000000
结果:
D[0]   D[1]   D[2]   D[3]   D[4]   D[5]
 0   1000000   10     50     30     60
*/
#include <iostream>
#include <cstdio>
#define MAX 1000000
using namespace std;
int arcs[10][10];//邻接矩阵
int D[10];//保存最短路径长度
int p[10][10];//路径
int final[10];//若final[i] = 1则说明 顶点vi已在集合S中
int n = 0;//顶点个数
int v0 = 0;//源点
int v,w;
void ShortestPath_DIJ()
{
     for (v = 0; v < n; v++) //循环 初始化
     {
          final[v] = 0; D[v] = arcs[v0][v];
          for (w = 0; w < n; w++) p[v][w] = 0;//设空路径
          if (D[v] < MAX) {p[v][v0] = 1; p[v][v] = 1;}
     }
     D[v0] = 0; final[v0]=1; //初始化 v0顶点属于集合S
     //开始主循环 每次求得v0到某个顶点v的最短路径 并加v到集合S中
     for (int i = 1; i < n; i++)
     {
          int min = MAX;
          for (w = 0; w < n; w++)
          {
               //我认为的核心过程--选点
               if (!final[w]) //如果w顶点在V-S中
               {
                    //这个过程最终选出的点 应该是选出当前V-S中与S有关联边
                    //且权值最小的顶点 书上描述为 当前离V0最近的点
                    if (D[w] < min) {v = w; min = D[w];}
               }
          }
          final[v] = 1; //选出该点后加入到合集S中
          for (w = 0; w < n; w++)//更新当前最短路径和距离
          {
               /*在此循环中 v为当前刚选入集合S中的点
               则以点V为中间点 考察 d0v+dvw 是否小于 D[w] 如果小于 则更新
               比如加进点 3 则若要考察 D[5] 是否要更新 就 判断 d(v0-v3) + d(v3-v5) 的和是否小于D[5]
               */
               if (!final[w] && (min+arcs[v][w]<D[w]))
               {
                    D[w] = min + arcs[v][w];
                   // p[w] = p[v];
                    p[w][w] = 1; //p[w] = p[v] + [w]
               }
          }
     }
}
 
 
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
         for (int j = 0; j < n; j++)
         {
              cin >> arcs[i][j];
         }
    }
    ShortestPath_DIJ();
    for (int i = 0; i < n; i++) printf("D[%d] = %d\n",i,D[i]);
    return 0;
}

3.spaf

	
#include<iostream>
#include<vector>
#include<list>
using namespace std;
struct Edge
{
    int to,len;
};
bool spfa(const int &beg,//出发点
          const vector<list<Edge> > &adjlist,//邻接表,通过传引用避免拷贝
          vector<int> &dist,//出发点到各点的最短路径长度
          vector<int> &path)//路径上到达该点的前一个点
//没有负权回路返回0
//福利:这个函数没有调用任何全局变量,可以直接复制!
{
    const int INF=0x7FFFFFFF,NODE=adjlist.size();//用邻接表的大小传递顶点个数,减少参数传递
    dist.assign(NODE,INF);//初始化距离为无穷大
    path.assign(NODE,-1);//初始化路径为未知
    list<int> que(1,beg);//处理队列
    vector<int> cnt(NODE,0);//记录各点入队次数,用于判断负权回路
    vector<bool> flag(NODE,0);//标志数组,判断是否在队列中
    dist[beg]=0;//出发点到自身路径长度为0
    cnt[beg]=flag[beg]=1;//入队并开始计数
    while(!que.empty())
    {
        const int now=que.front();
        que.pop_front();
        flag[now]=0;//将当前处理的点出队
        for(list<Edge>::const_iterator//用常量迭代器遍历邻接表
                i=adjlist[now].begin(); i!=adjlist[now].end(); ++i)
            if(dist[i->to]>dist[now]+i->len)//不满足三角不等式
            {
                dist[i->to]=dist[now]+i->len;//更新
                path[i->to]=now;//记录路径
                if(!flag[i->to])//若未在处理队列中
                {
                    if(NODE==++cnt[i->to])return 1;//计数后出现负权回路
                    if(!que.empty()&&dist[i->to]<dist[que.front()])//队列非空且优于队首(SLF)
                        que.push_front(i->to);//放在队首
                    else que.push_back(i->to);//否则放在队尾
                    flag[i->to]=1;//入队
                }
            }
    }
    return 0;
}
int main()
{
    int n_num,e_num,beg;//含义见下
    cout<<"输入点数、边数、出发点:";
    cin>>n_num>>e_num>>beg;
    vector<list<Edge> > adjlist(n_num,list<Edge>());//默认初始化邻接表
    for(int i=0,p; i!=e_num; ++i)
    {
        Edge tmp;
        cout<<"输入第"<<i+1<<"条边的起点、终点、长度:";
        cin>>p>>tmp.to>>tmp.len;
        adjlist[p].push_back(tmp);
    }
    vector<int> dist,path;//用于接收最短路径长度及路径各点
    if(spfa(beg,adjlist,dist,path))cout<<"图中存在负权回路\n";
    else for(int i=0; i!=n_num; ++i)
        {
            cout<<beg<<"到"<<i<<"的最短距离为"<<dist[i]<<",反向打印路径:";
            for(int w=i; path[w]>=0; w=path[w])cout<<w<<"<-";
            cout<<beg<<'\n';
        }
}
/*
 * 单源最短路SPFA 
 * 采用邻接表存图	
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

#define REP(I, A, B) for(int I = (A); I <= (B); I++)
const int INF = 0x7ffffff;
const int MAXN = 1e5 + 5;
const int MAXM = 5e5 + 5;

int tot, n, m, s;
int d[MAXN], head[MAXN], nxt[MAXM], val[MAXM], to[MAXM], path[MAXN];
bool vis[MAXN];
queue<int> q;

inline void AddEdge(int u, int v, int w) {
	nxt[++tot] = head[u]; 
	to[head[u] = tot] = v; 
	val[tot] = w;
}

inline int read() {
	char ch = getchar(); 
	int ans = 0;
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)) ans = ans * 10 + (ch - '0'), ch = getchar();
	return ans;
}

void print_path(int v) {
	if(path[v]) print_path(path[v]);
	printf("%d ", v);
}

int main() {
	n = read(), m = read(); s = 1;
	REP(i, 1, m) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c), AddEdge(b, a, c);
	}
	// SPFA
	REP(i, 1, n) d[i] = INF;
	vis[s] = 1; q.push(s); d[s] = 0;
	while(!q.empty()) {
		int t = q.front(); q.pop(); vis[t] = 0;
		for(int e = head[t]; e; e = nxt[e]) {
			if(d[to[e]] > d[t] + val[e]) {
				d[to[e]] = d[t] + val[e]; path[to[e]] = t;
				if(!vis[to[e]]) vis[to[e]] = 1, q.push(to[e]);
			}
		}
	}
	
	REP(i, 1, n) printf("Node: %d | Len: %d | Path:", i, d[i]); //print_path(i), puts("");
	return 0;
}

4.算法解析( TODO)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值