P2176 [USACO11DEC] RoadBlock S / [USACO14FEB] Roadblock G/S 题解

P2176 [USACO11DEC] RoadBlock S / [USACO14FEB] Roadblock G/S

简化题意

无向图有 \(N\) 个点和 \(M\) 条边。第 \(i\) 条边连接节点 \(u_i\)\(v_i\),长度为 \(w_i\)。我们可以选择一条边,将它的长度翻倍。

求最大化节点 \(1\) 到节点 \(N\) 的最短距离的增量。

解题思路

在个人模拟训练中,我的解题思路是用 Floyd 求一遍正常情况下的最短路,然后记录每次操作后节点 \(1\)\(N\) 的距离。最后取最小和次小的距离,相减得到答案。对于只有 \(N - 1\) 条边的情况,我们用 BFS 求一遍最短路并记录其中经过的最大边长,最后输出此边长即可。

这个思路写好代码交上去只有 \(50\) 分,后来我发现是我考虑的不全面。因为这样其实根本体现不出修改一条边长度的意义。

后来,我没啥思路,就想到先暴力骗分。我的暴力思路就是直接在 Dijkstra 操作的过程中,标记两个点。如果这两个点都用到了,就将它们对应的边记录两倍的长度。其他的操作都按照正常的 Dijkstra 来做。

这样的话,时间复杂度是 \(O(M \times (M + N) \times \log N)\)

然后,由于限制是 \(N \leq 100\)\(M \leq 5000\),所以我们发现其实这个暴力是可以过的……

那么,详细就看我的代码吧。记住,有的时候如果你冲不出最优解,就先试试暴力,说不定拿的分数也挺可观的。

代码部分

#include <bits/stdc++.h>
using namespace std;
const int INF = 1 << 30;
int n, m, ans = 0;
int d[110];
bool vis[110];
vector<pair<int, int> > g[110], lst;
void dijkstra(int x, int y)
{
	for(int i = 2;i <= n;i ++)
		d[i] = INF;
	d[1] = 0;
	priority_queue<pair<int, int> > q;
	q.push({0, 1});
	
	while(!q.empty())
	{
		int u = q.top().second;
		q.pop();
		if(vis[u])
			continue;
		for(int i = 0;i < g[u].size();i ++)
		{
			int v = g[u][i].first, w = g[u][i].second;
			if(u == x && v == y || u == y && v == x)
				w *= 2;
			if(d[v] > d[u] + w)
			{
				d[v] = d[u] + w;
				q.push({-d[v], v});
			}
		}
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1, u, v, w;i <= m;i ++)
	{
		cin >> u >> v >> w;
		g[u].push_back({v, w});
		g[v].push_back({u, w});
		lst.push_back({u, v});
	}
	for(int i = 0;i < lst.size();i ++)
	{
		dijkstra(lst[i].first, lst[i].second);
		ans = max(ans, d[n]);
	}
	dijkstra(0, 0);
	cout << ans - d[n] << endl;
	
	return 0;
}

posted on 2026-02-17 20:04  Leo_29  阅读(6)  评论(0)    收藏  举报

导航