CF1737D(贪心,floyd)
CF1737D(贪心,floyd)
题意
带权无向图,要从1到n代价最小。有一个“魔法”对图修改:把某条边连到和他直接相连的另一个点上(a->b,b->c,就可以a->c)。代价为该边权值。
求在使用若干次魔法后的最小权。
思路
从结果上来看,通过若干次魔法后,调整的边一定是要走的,否则操作没有意义。对于调整后的最短路,如果边的个数大于1显然我们还是可以进一步贪心,取该最短路上的权最小边扩展,直到最短路边仅为1,此时对答案有贡献的就是这条边和对它的操作次数。
那么我们枚举所有边作为贡献边,只要再算出扩展这条边的操作次数就能算出答案。如果该边作为最短路上的一条边,扩展次数就是从该边走到1,n两点的边数。如果不在最短路径上,可以有这样的过程:先将该边的两个端点移动到某一个中间点,再从该点将两端点移动到1,n两点上。这个过程走的边数是中间点到1,n两点的边数加上该边两点到中间点需要的边数。
这些边数可以用 \(floyd\) 预处理。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<bitset>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<cassert>
#include<functional>
#include<iomanip>
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3fll
#define endl '\n'
#define ll long long
#define int long long
#define SZ(x) (int)x.size()
#define rep(i,a,n) for(int i = (a);i <= (n);i++)
#define dec(i,n,a) for(int i = (n);i >= (a);i--)
using namespace std;
using PII = array<int,2>;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
const int N =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n,m; cin >> n >> m;
vector<tuple<int,int,int>> e;
vector f(n + 1, vector<int>(n + 1,inf));
rep(i,1,m) {
int u,v,w; cin >> u >> v >> w;
e.emplace_back(u,v,w);
f[u][v] = f[v][u] = 1;
}
rep(i,1,n) f[i][i] = 0;
rep(k,1,n) rep(i,1,n) rep(j,1,n) f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
ll ans = linf;
for(auto &[u,v,w] : e) {
// 在最短路上
ans = min(ans, 1ll * w * min(f[1][u] + 1 + f[v][n], f[1][v] + 1 + f[u][n]));
// for 中间点
rep(i,1,n) ans = min(ans, 1ll * w * (f[i][1] + f[i][n] + min(f[u][i], f[v][i]) + 2));
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}