Homework Introduction

// 负环:
// 如果一个环上,环上各边的权值之和是负数
// 负环
// spfa 判负环

// 1, 队列,如果一个点入队出队次数超过 n

// 2. dis[v] , cnt[v] 记录到达 v 这个点经过几条边
//       如果经过 的边的数量大于等于 n ,负环

bool spfa() {
	queue<int> q;
	memset(vis, 0, sizeof vis);
	memset(dis, 0x3f, sizeof dis);
	memset(cnt, 0, sizeof cnt);
	dis[1] = 0;
	q.push(1)
	vis[1] = 1;
	while (q.size()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for (int i = head[u]; i ; i = nxt[i]) {
			int v = ver[i];
			int w = edge[i];
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				// cnt[v] 用来统计到达 v 的时候经过的边的数量
				cnt[v] = cnt[u] + 1; // 多了一条边
				if (cnt[v] >= n)
					return 1; // 存在负环
				if (vis[v])
					continue;
				vis[v] = 1;
				q.push(v);
			}
		}
	}
	return 0;
}
// 边可以重复经过
// 严格最短路

//从 1 - i 来了一个路径,长度 是 x
//1. 比之前的最短路短, 新的第一名
//	之前的最短路变成次短路,
//	x 变成新的最短路
//2. 比之前的最短路长,比次短路短 新的第二名
//	x 变成次短路
//3. x 比次短路长, 丢了

//int dis[N][2];
//dis[i][0] 记录到 i 的最短路长度
//dis[i][1] 记录到 i 的次短路长度
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 10;
const int M = 1e5 + 10;
int n, m;
int head[N], ver[M << 1], edge[M << 1], nxt[M << 1], tot;
int dis[N][2];

void add(int x, int y, int z) {
	tot++;
	ver[tot] = y;
	edge[tot] = z;
	nxt[tot] = head[x];
	head[x] = tot;
}

void dijkstra() {
	memset(dis, 0x3f, sizeof dis);
	dis[1][0] = 0;
	priority_queue<pair<int, int>>q;
	q.push({0, 1});
	while (q.size()) {
		int u = q.top().second;
		int s = -q.top().first;
		q.pop();
		// 直接舍弃
		if (s > dis[u][1])
			continue;
		for (int i = head[u] ; i ; i = nxt[i]) {
			int v = ver[i];
			int w = edge[i];
			if (dis[v][0] > s + w) { // 新的第一名
				dis[v][1] = dis[v][0];
				dis[v][0]  = s + w;
				q.push({-dis[v][0], v});
				q.push({-dis[v][1], v});
			}
			if (s + w > dis[v][0] && s + w < dis[v][1]) {
				dis[v][1] = s + w;
				q.push({-dis[v][1], v});
			}
		}
	}
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		add(x, y, z);
		add(y, x, z);
	}
	dijkstra();
	cout << dis[n][1];
	return 0;
}
// 次短路:第二短的路
// 最短路,
// 分类:
// 1. 边可以重复经过; 边不可以重复经过
// 2. 严格次短路;非严格次短路,

// 边不可以重复经过
//// 删边法:
//1. 跑一遍最短路,记录下来所有的边
//// pre 数组,记录到达当前点之前的点
//// 边 pre[i] , i
//2. 依次删除路径里面的边,再跑最短路
//3. 取一个最小值,最小的值就是次短路
//a <b  严格小  1 2
//a <= b 非严格 1 1
// 非严格最短路,边不可以重复经过
// 删边法
//两点之间的距离公式
//sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
#include <bits/stdc++.h>
using namespace std;
const int N = 2e2 + 10;
const int M = 2e4 + 10;
int head[N], ver[M], nxt[M],  tot;
double edge[M];
int x[N], y[N];  // 记录点的坐标
bool vis[N];
double dis[N];

void add(int x, int y, double z) {
	tot++;
	edge[tot] = z;
	nxt[tot] = head[x];
	head[x] = tot;
	ver[tot] = y;
}

double f(int i, int j) {
	double sum = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
	return sqrt(sum);
}

int n, m, pre[N];  // pre  记录来的点,记录最短路径经过的点

void dijkstra(int dx, int dy) {
	memset(vis, 0, sizeof vis);
	for (int i = 1; i <= n; i++)
		dis[i] = 1e9;
	dis[1] = 0;
	priority_queue<pair<double, int>> q;
	q.push({0, 1});
	while (q.size()) {
		int u =  q.top().second;
		q.pop();
		if (vis[u])
			continue;
		vis[u] = 1;
		for (int i = head[u] ; i ; i = nxt[i]) {
			int v = ver[i];
			double w  = edge[i];
			// 判断是不是要删除的边,如果是就删除
			if (u == dx && v == dy || u == dy && v == dx)
				continue;
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				q.push({-dis[v], v});
				// 记录前驱节点
				if (dx == -1 && dy == -1)
					pre[v] = u;
			}
		}
	}
}


int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> x[i] >> y[i];
	for (int i = 1; i <= m; i++) {
		int xx, yy;
		cin >> xx >> yy;
		double w = f(xx, yy);
		add(xx, yy, w);
		add(yy, xx, w);
	}
	// 跑一个原始最短路
	dijkstra(-1, -1); // -1 -1 ,代表不用删除边,
	double res = 1e9;
	// 遍历要删除的边,删除跑最短路
	for (int i = n; i != 1 ; i = pre[i]) {
		dijkstra(i, pre[i]);
		res = min(res, dis[n]);
	}
	if (res == 1e9)
		cout << -1;
	else
		printf("%.2lf", res);
	return 0;
}
/*
  对于没有速度的点来说,到达这个点的速度是有很多种可能的
  因此记录最短路的时候,也记录下来到达这个点的时候的速度

  分层图最短路,但是分层图不好建立
  所以我们通过 f[N][V] 来体现分层图
 */
#include <bits/stdc++.h>
using namespace std;
const int N = 200;
const int M = 22510;
int n, m, d;
// v 记录的当前点速度,edge 记录的是当前的长度
int head[N], ver[M], edge[M], value[M], nxt[M], tot;
// 求的是最短的时间, edge / v
double f[N][501];
bool vis[N][501];

pair<int, int> ans[N][501]; // 记录的是到达点i和速度为v的点的上一个转移来的位置

void add(int a, int b, int vv, int l) {
	tot++;
	ver[tot] = b;
	edge[tot] = l;
	value[tot] = vv;
	nxt[tot] = head[a];
	head[a] = tot;
}

void out(int x, int y) { // 递归输出
	if (x == 0) {
		cout << "0 ";
		return;
	}
	out(ans[x][y].first, ans[x][y].second);
	cout << x << " ";
}

void dijkstra() {
	for (int i = 0; i < n; i++)
		for (int j = 0; j < 501; j++)
			f[i][j] = 1e9;
	f[0][70] = 0;  // 注意,起点是 0
	// pair<时间,pair<点, 速度>>
	priority_queue<pair<double, pair<int, int >>> q;
	q.push({0, {0, 70} });
	while (q.size()) {
		int u = q.top().second.first;
		int speed = q.top().second.second;
		q.pop();
		if (vis[u][speed]) continue;
		vis[u][speed] = 1;
		// 转移
		for (int i = head[u] ; i ; i = nxt[i]) {
			int v = ver[i];
			// 如果当前点有速度就是当前的速度,如果没有就是上一个的速度
			int ns = value[i] ? value[i] : speed;
			int l = edge[i];
			double time = l * 1.0 / ns;
			if (f[v][ns] > f[u][speed] + time) {
				f[v][ns] = f[u][speed] + time;
				ans[v][ns] = {u, speed};
				q.push({-f[v][ns], {v, ns}});
			}
		}

	}
}

int main() {
	cin >> n >> m >> d;
	for (int i = 1; i <= m; i++) {
		int a, b, v, l;
		cin >> a >> b >> v >> l;
		add(a, b, v, l);
	}

	dijkstra();

	int j = 0;
	// 找最短时间
	for (int i = 0; i < 501; i++)
		if (f[d][i] < f[d][j])
			j = i;
	out(d, j);
	return 0;
}
Status
Done
Problem
17
Open Since
2026-1-21 0:00
Deadline
2026-2-28 23:59
Extension
24 hour(s)