2023杭电多校 Circuit

文章介绍了一种利用Floyd算法解决图论中的经典最小环问题的方法。在Floyd遍历过程中,通过三层循环更新节点间的最短路径,并利用环的性质避免重复计算。代码示例展示了如何实现这一算法,并特别提到了在ACM竞赛中处理这类问题的策略。
摘要由CSDN通过智能技术生成

https://acm.hdu.edu.cn/showproblem.php?pid=7322

经典最小环问题。可以用floyd求解。在floyd中,三层循环k,i,j,此时利用k更新i->j的距离,那么如果j->i有边,那么就会构成环。所以d[i][j]表示i->j的最短距离,cnt[i][j]表示i->j最短路径的条数,d[i][j]表示初始状态下的i->j的边。同时,为了避免对一个环的重复计算,我们考虑在环的编号最大的结点去计算,这是由于floyd本身是个dp的做法,完整形式应该是dp[k][i][j],表示从i->j,由不超过k的结点更新的最短距离,也就是说,除了i,j以外,路径上的点不超过k。所以,只需要当枚举到最外层的k时,在枚举环上的另外一个与k相邻的结点即可,此时,这个环的所有结点全都不超过k。不重不漏。注意acwing的观光之旅这道题目,只需要考虑最小环的长度,不需要记录数量,所以,并没有真的按照在环上的最大节点去更新答案,会重复计算。

#include <bits/stdc++.h>
#define LL long long
#define int LL
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define x first
#define y second
#define kanm7 ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 510, mod = 998244353;
int g[N][N], cnt[N][N], d[N][N];
int n, m, ans = 1e18, res = 0;

void floyd() {
	for(int k = 1; k <= n; k ++) {
		for(int i = 1; i <= n; i ++) {
			for(int j = 1; j <= n; j ++){
				if(g[i][k] + g[k][j] < g[i][j]) {
					g[i][j] = g[i][k] + g[k][j];
					cnt[i][j] = cnt[i][k] * cnt[k][j] % mod;
				} else if(g[i][k] + g[k][j] == g[i][j]) {
					cnt[i][j] += cnt[i][k] * cnt[k][j] % mod;
					cnt[i][j] %= mod;
				}
			}
		}
		for(int i = 1; i < k; i ++) {
			if(d[k][i]) {
				if(d[k][i] + g[i][k] < ans) {
					ans = d[k][i] + g[i][k];
					res = cnt[i][k];
				} else if(d[k][i] + g[i][k] == ans) {
					res += cnt[i][k];
					res %= mod;
				}
			}
		}
	}
}

void solve() {
	cin >> n >> m;
	ans = 1e18;
	res = 0;
	for(int i = 0; i <= n; i ++) {
		for(int j = 0; j <= n; j ++) {
			g[i][j] = 1e18;
			cnt[i][j] = 0;
			d[i][j] = 1e18;
		}
		g[i][i] = 0;
		d[i][i] = 0;
	}
	while(m --) {
		int a, b, c; cin >> a >> b >> c;
		g[a][b] = c;
		d[a][b] = c;
		cnt[a][b] = 1;
	}
	floyd();
	if(ans < 1e18) {
		cout << ans << ' ' << res % mod << endl;
	} else cout << -1 << ' ' << -1 << endl;
}

signed main() {
	kanm7;
	int T; cin >> T;
	while(T --) {
		solve();
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kanm7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值