Floyd-Warshall & Dijkstra & spfa
最近在强化自己的图论算法,首先翻看以前的图论题目(此题需翻墙)
Google APAC 2016 University Graduates Test Round A:Problem C. gCampus
此题应用最短路径的Floyd-Warshall算法可以轻松解决,可惜当时没能熟练掌握,大数据挂了。
于是恶补之,参考:
http://blog.chinaunix.net/uid-26548237-id-3834873.html
- Floyd-Warshall算法应用了最短路径的哪些性质?
A到B 的最短路如果经过 C,那么其路径一定是 A到C 和 C到B 的最短路相连接形成的。
所以在记录路径的时候(那个记录路径的矩阵),记录的是 u到v 的最短路径所经过的第一个顶点,比如记为 k1,那么寻找路径的时候,第一步可以找到 k1,然后继续寻找 k1到v 的最短路径即可。
- Dijkstra算法
与最小生成树方法的过程相同,只是 松弛 的记录方法和代表的意义略有区别。最小生成树记录的是每个点到最小生成树的距离,最短路记录的是每个节点到源点的最短路径的距离。
- spfa算法
这是改进的 Bellman-Ford 算法,通过优先队列控制松弛次数,使得循环可以提前退出。也是单源最短路的经典算法!
附上通过代码(Floyd-Warshall):
#include <stdio.h>
#define INT_MAX 1e14
#define min(a,b) (((a) < (b)) ? (a) : (b))
int T, ti, i, j, k, x, y, d;
int N, M;
long long mpi[10001][3], dst[101][101];
//mpi 记录每条边的起点,终点,权重
//dst 记录距离矩阵
void init() {
/*
初始化距离矩阵
*/
for (i = 0; i < 101; i++) {
for (j = 0; j < 101; j++) {
dst[i][j] = INT_MAX;
}
}
for (i = 0; i < 101; i++) {
dst[i][i] = 0;
}
}
int main() {
scanf("%d", &T);
for (ti = 1; ti <= T; ti++) {
init();
scanf("%d %d", &N, &M);
for (i = 0; i < M; i++) {
scanf("%d %d %d", &x, &y, &d);
mpi[i][0] = x;
mpi[i][1] = y;
mpi[i][2] = d;
dst[x][y] = min(dst[x][y], d);
dst[y][x] = min(dst[y][x], d);
//输入注意处理:重复输入!
}
//计算距离矩阵,应用Floyd算法
for (k = 0; k < N; k++) {
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++) {
if (dst[i][k] != INT_MAX && dst[k][j] != INT_MAX &&
dst[i][k] + dst[k][j] < dst[i][j]) {
dst[i][j] = dst[i][k] + dst[k][j];
}
}
}
}
printf("Case #%d:\n", ti);
int ok;
for (k = 0; k < M; k++) {
x = mpi[k][0];
y = mpi[k][1];
ok = 1;
//寻找无效边,构造输出
for (i = 0; i < N && ok; i++) {
for (j = 0; j < N && ok; j++) {
if (dst[i][j] == dst[i][x] + mpi[k][2] + dst[y][j]) {
ok = 0;
}
}
}
if (ok) {
printf("%d\n", k);
}
}
}
return 0;
}
附上通过代码(Dijkstra):
#include <stdio.h>
#include <string.h>
#define INT_MAX 1e14
#define min(a,b) (((a) < (b)) ? (a) : (b))
int T, ti, i, j, k, x, y, d;
int N, M;
long long mpi[10001][3], dst[101][101], rt[101][101], vis[101];
//mpi 记录每条边的起点,终点,权重
//dst 记录距离矩阵
//rt 记录单源最短路中每个源点到终点的距离
//vis 标记节点访问情况
void init() {
/*
初始化距离矩阵
*/
for (i = 0; i < 101; i++) {
for (j = 0; j < 101; j++) {
dst[i][j] = INT_MAX;
}
}
for (i = 0; i < 101; i++) {
dst[i][i] = 0;
}
}
int main() {
scanf("%d", &T);
for (ti = 1; ti <= T; ti++) {
init();
scanf("%d %d", &N, &M);
for (i = 0; i < M; i++) {
scanf("%d %d %d", &x, &y, &d);
mpi[i][0] = x;
mpi[i][1] = y;
mpi[i][2] = d;
dst[x][y] = min(dst[x][y], d);
dst[y][x] = min(dst[y][x], d);
//输入注意处理:重复输入!
}
//计算每个点的单源最短路的距离,应用Dijstra算法
int pre, imin;
long long nmin;
for (k = 0; k < N; k++) {
for (i = 0; i < N; i++) rt[k][i] = INT_MAX;
rt[k][k] = 0;
memset(vis, 0, sizeof(vis));
vis[k] = 1;
//求以k为源点的单源最短路
pre = k;
for (i = 1; i < N; i++) {
for (j = 0; j < N; j++) {
if (vis[j] == 1) continue;
if (rt[k][pre] + dst[pre][j] < rt[k][j]) {
rt[k][j] = rt[k][pre] + dst[pre][j];
}
}
nmin = INT_MAX;
imin = -1;
for (j = 0; j < N; j++) {
if (vis[j] == 0 && rt[k][j] < nmin) {
nmin = rt[k][j];
imin = j;
}
}
vis[imin] = 1;
pre = imin;
}
}
printf("Case #%d:\n", ti);
int ok;
for (k = 0; k < M; k++) {
x = mpi[k][0];
y = mpi[k][1];
ok = 1;
//寻找无效边,构造输出
for (i = 0; i < N && ok; i++) {
for (j = 0; j < N && ok; j++) {
if (rt[i][j] == rt[i][x] + mpi[k][2] + rt[y][j]) {
ok = 0;
}
}
}
if (ok) {
printf("%d\n", k);
}
}
}
return 0;
}
附上通过代码(spfa):
#include <stdio.h>
#include <string.h>
#include <queue>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define MAX 0x7F
using namespace std;
int T, Ti, N, M, x, y, d, i, j, k;
long long pt[10001][3], mp[101][101], dis[101][101];
int vis[101];
int main() {
scanf("%d", &T);
for (Ti = 1; Ti <= T; Ti++) {
scanf("%d %d", &N, &M);
//map input
memset(mp, MAX, sizeof(mp));
for (i = 0; i < M; i++) {
scanf("%d %d %d", &x, &y, &d);
mp[x][y] = min(mp[x][y], d);
mp[y][x] = min(mp[y][x], d);
pt[i][0] = x;
pt[i][1] = y;
pt[i][2] = d;
}
//spfa
memset(dis, MAX, sizeof(dis));
for (k = 0; k < N; k++) {
queue<int> que;
que.push(k);
memset(vis, 0, sizeof(vis));
vis[k] = 1;
dis[k][k] = 0;
while (!que.empty()) {
int tp = que.front();
que.pop();
for (i = 0; i < N; i++) {
if (mp[tp][i] == MAX) continue;
if (dis[k][i] > dis[k][tp] + mp[tp][i]) {
dis[k][i] = dis[k][tp] + mp[tp][i];
if (vis[i] == 0) {
vis[i] = 1;
que.push(i);
}
}
}
vis[tp] = 0;
}
}
//find inefficient road
printf("Case #%d:\n", Ti);
int ok;
for (k = 0; k < M; k++) {
x = pt[k][0];
y = pt[k][1];
d = pt[k][2];
ok = 1;
for (i = 0; i < N && ok; i++) {
for (j = 0; j < N && ok; j++) {
if (dis[i][j] == dis[i][x] + d + dis[y][j]) {
ok = 0;
}
}
}
if (ok) {
printf("%d\n", k);
}
}
}
return 0;
}