• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【Floyd】AtCoder ABC375 F. Road Blocked

前言

Floyd 算法(弗洛伊德算法)是解决图论中全源最短路径问题的一种经典算法。全源最短路径问题是指对于给定的一个带权有向图(也可以是无向图,将无向图看作双向有向图即可),求出每一对顶点之间的最短路径长度。Floyd 算法是动态规划思想的一个典型应用,动态规划的过程是引入一个中转节点来松弛路径。

题目

https://atcoder.jp/contests/abc375/tasks/abc375_f

题意

第一行,输入三个正整数 \(n,m,q(2 \leq n\leq300,0 \leq m \leq \frac{n \times (n-1)}{2},1 \leq q \leq 2 \times 10^5)\),代表一张 \(n\) 个节点 \(m\) 条无向边的图和进行询问的次数;

接下来 \(m\) 行,每行输入三个正整数 \(x_i,y_i,w_i(1 \leq x_i < y_i \leq n, 1 \leq w_i \leq 10^9)\),代表节点 \(x_i\) 和 \(y_i\) 之间连接了一条长度为 \(w_i\) 的无向边;

最后输入 \(q\) 行,输入的类型有:

  • 1 i:将输入的第 \(i\) 条边删除(永久生效)
  • 2 x y:询问图中节点 \(x,y\) 之间的最短距离

对于每个类型 2 的询问,都输出最短距离。若无论如何无法抵达,则输出 \(-1\)。

保证:无向图中没有重边和自环;类型 1 的询问至多只有 min\((q, 300)\) 次。

题解

对于询问类型 2,本质是询问全源最短路径长度,Floyd 算法时间复杂度为\(O(n^3)\),满足该题时间复杂度要求,因此可以往 Floyd 算法的方向思考。

使用 Floyd 算法维护全源最短路径长度,初始化操作是将全部路径置为无穷大。对于操作类型 1,相当于重新将一条无向边置为无穷大。

但此时引入一个新的问题:若按照询问的顺序进行删边,由于所删边的节点可能是其他最短路径的中转接点,则可能影响到多条路径,故不得不重新跑一边 Floyd 算法。类型 1 的询问次数在 min(\(q,300\)) 次以内,时间复杂度为 \(O(n^4)\)。e8 级别的时间复杂度是比较危险的时间复杂度,如果不是实在别无选择,不建议交这种比较危险的复杂度的题解,容易 tle。

删除一条边,不得不重新将 Floyd 算法重跑一遍,单次操作时间复杂度为 \(O(n^3)\)。而添加一条边,相当于引入两个中转节点,Floyd 算法每次引入一个中转节点,单次操作时间复杂度为 \(O(n^2)\)。

反向执行一次类型 1 的操作,相当于更新一条无向边的长度,本质是更新了两个中转节点,执行 min(\(q, 300\)) 次的时间复杂度来到 \(O(n^3)\),也是满足要求的时间复杂度。

因此,可以反向考虑执行类型 1 的操作。具体的算法步骤如下:

  1. 将一张无向图的全部边均置为无穷大;
  2. 将全部从未删除的边初始化到无向图中;
  3. 执行 Floyd 算法,计算出全源最短路径长度;
  4. 反向执行询问操作,对于类型 2 的询问,直接输出最短路径长度;对于类型 1 的询问,尝试更新无向边的长度,若当前要更新的长度大于当前无向边长度,则放弃更新,否则更新,并以该无向边的两个节点作为中转节点进行更新全源最短路径长度。

时间复杂度:\(O(n^3)\)
空间复杂度:\(O(n^2)\)

参考代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

constexpr ll INF = 1e18;
int n, m, q;
ll a[301][301];
bool d[45007];

void floyd_(int k) {//引入节点 k 作为中转节点 
	for (int i = 1; i <= n; ++ i) {
		if (i == k) continue;
		for (int j = 1; j <= n; ++ j) {
			a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
		}
	}
}

int main() {
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	cin >> n >> m >> q;
	vector<int> x(m + 1), y(m + 1), w(m + 1), t(q), u(q), v(q);
	for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) a[i][j] = INF;
	for (int i = 1; i <= m; ++ i) cin >> x[i] >> y[i] >> w[i];
	for (int i = 0; i < q; ++ i) {
		cin >> t[i] >> u[i];
		if (t[i] == 2) cin >> v[i];
		else d[u[i]] = true;
	}
	for (int i = 1; i <= m; ++ i) if (!d[i]) a[x[i]][y[i]] = a[y[i]][x[i]] = w[i];
	for (int k = 1; k <= n; ++ k) floyd_(k);
	stack<ll> stk;
	for (int i = q - 1; i >= 0; -- i) {
		if (t[i] == 2) stk.emplace(a[u[i]][v[i]] == INF ? -1 : a[u[i]][v[i]]);
		else {
			int &id = u[i];
			bool flag = false;
			if (a[x[id]][y[id]] > w[id]) {
				a[x[id]][y[id]] = w[id];
				flag = true;
			} 
			if (a[y[id]][x[id]] > w[id]) {
				a[y[id]][x[id]] = w[id];
				flag = true;
			}
			if (flag) floyd_(x[id]), floyd_(y[id]);
		}
	}
	while (!stk.empty()) {
		cout << stk.top() << '\n';
		stk.pop();
	}
	return 0;
}

posted on 2025-01-20 19:28  RomanLin  阅读(45)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3