CF1737D(贪心,floyd)

CF1737D(贪心,floyd)

Problem - D - Codeforces

题意

带权无向图,要从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;
}
posted @ 2022-10-09 15:08  Mxrurush  阅读(48)  评论(0编辑  收藏  举报