1.dijkstra算法
①求从source到destination的最短路径(最短距离、最短路径条数、最短路径上的节点数以及路径)
②若最短路径不唯一,则再求从source到destination的最小代价
③若最短路径还是不唯一,则再求从source到destination的最大节点权值和
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <climits>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 500
int d[MAXN][MAXN]; //d[i][j]-从i到j的距离 (由题目给出)
int c[MAXN][MAXN]; //c[i][j]-从i到j的代价 (由题目给出)
int w[MAXN]; //w[i]-i的权值(由题目给出)
int dis[MAXN]; //dis[i]-从起点到i的最短距离
int cost[MAXN]; //cost[i]-从起点到i的最小代价
int maxw[MAXN]; //maxw[i]-从起点到i的最大权值
int cnt[MAXN]; //cnt[i]-从起点到i的最短路径条数
int num[MAXN]; //num[i]-从起点到i的最短路径上的节点个数
int pre[MAXN]; //pre[i]-最短路径上i的前一个节点
int vis[MAXN]; //vis[i]-i是否被访问过
void dijkstra(int source, int destination, int n) //source-起点 destination-终点 n-节点数
{
for (int i = 0; i < n; i++)
{
dis[i] = INF;
cost[i] = INF;
maxw[i] = 0;
cnt[i] = 0;
num[i] = 0;
pre[i] = -1;
vis[i] = 0;
}
dis[source] = 0;
cost[source] = 0;
maxw[source] = w[source];
cnt[source] = 1;
num[source] = 1;
for (int i = 0; i < n; i++)
{
int min = INF, k = -1;
for (int j = 0; j < n; j++)
{
if (!vis[j] && dis[j] < min)
{
min = dis[j];
k = j;
}
}
if (k == -1) //没有符合条件的节点则返回
return;
vis[k] = 1;
if (k == destination) //终点已求得最短路径则返回
return;
for (int j = 0; j < n; j++)
{
if (!vis[j] && d[k][j] != INF)
{
if (dis[k] + d[k][j] < dis[j]) //从起点经过k到j的距离比从起点到j的距离短
{
dis[j] = dis[k] + d[k][j];
cost[j] = cost[k] + c[k][j];
maxw[j] = maxw[k] + w[j];
cnt[j] = cnt[k];
num[j] = num[k] + 1;
pre[j] = k;
}
else if (dis[k] + d[k][j] == dis[j])
{
cnt[j] += cnt[k];
if (cost[k] + c[k][j] < cost[j]) //在距离相同的情况下,从起点经过k到j的代价比从起点到j的代价小
{
cost[j] = cost[k] + c[k][j];
maxw[j] = maxw[k] + w[j];
num[j] = num[k] + 1;
pre[j] = k;
}
else if (cost[k] + c[k][j] == cost[j] && maxw[k] + w[j] > maxw[j]) //在距离和代价相同的情况下,从起点经过k到j的节点权值比从起点到j的节点权值大
{
maxw[j] = maxw[k] + w[j];
num[j] = num[k] + 1;
pre[j] = k;
}
}
}
}
}
}
void print_path(int source, int x) //递归输出最短路径(不包括起点,因为输出第一个节点的时候不需要"->")
{
if (x == source)
return;
print_path(source, pre[x]);
cout << "->" << x;
}
int main()
{
int n, m;
cin >> n >> m; //n-节点数 m-边数
for (int i = 0; i < n; i++)
{
int v, ww;
cin >> v >> ww; //v-节点号 ww-权值
w[v] = ww;
}
memset(d, INF, sizeof(d));
memset(c, INF, sizeof(c));
for (int i = 0; i < m; i++)
{
int v, u, dd, cc;
cin >> v >> u >> dd >> cc; //v-边的起点 u-边的终点 dd-边的距离 cc-边的代价
d[v][u] = d[u][v] = dd;
c[v][u] = c[u][v] = cc;
}
int source, destination;
cin >> source >> destination;
dijkstra(source, destination, n);
cout << dis[destination] << " " << cost[destination] << " " << maxw[destination] << " " << cnt[destination] << " " << num[destination] << endl;
cout << source;
print_path(source, destination);
return 0;
}
2.并查集
一般用于:
①求该图的连通分支
②判断该图是否有环
③Kruskal算法
#define MAXN 1000
int f[MAXN];
void Init() //初始化每个节点的祖先都是自己
{
for (int i = 1; i <= n; i++)
f[i] = i;
}
int Find(int x) //查找节点x的祖先
{
if (x != f[x])
f[x] = Find(f[x]); //回溯时压缩路径,让每一个节点直接和其祖先连接
return f[x];
}
void Union(int x, int y) //合并两个分支
{
int a = Find(x);
int b = Find(y);
if (a != b)
f[a] = b;
}