P4716 【模板】最小树形图(朱刘算法模板题)

P4716 【模板】最小树形图

在这里插入图片描述

输入输出样例

输入 #1

4 6 1
1 2 3
1 3 1
4 1 2
4 2 2
3 2 1
3 4 1

输出 #1

3

输入 #2

4 6 3
1 2 3
1 3 1
4 1 2
4 2 2
3 2 1
3 4 1

输出 #2

4

输入 #3

4 6 2
1 2 3
1 3 1
4 1 2
4 2 2
3 2 1
3 4 1

输出 #3

1

在这里插入图片描述

总结

头一次了解到朱刘算法(大佬写的详解,强烈建议初学者观看,很详细)

半伪代码

while(true){
	步骤一:找到除了根节点外每个点被指向的最短距离的线,连接起来。
	步骤二未有其他点指向,有则继续下方步骤,无则跳出返回-1。
	步骤三:找到环,进行预处理。
	步骤四:判断是否存在环,,有则继续下方步骤,无则跳出返回总和。
	步骤五:将环变成一个点,并更新所有指向环里点的距离。
}

代码


#include <bits/stdc++.h>
#define ll long long
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
ll n, m, i, j, t, w, sum, k, flag;

struct node {
    ll be, en, va;
}a[2356];
//数组大小别想往下再减少
//别问为什么,问就是闲的

ll zhuliu(ll root) {

    ll sum = 0, dis[n + 1], pre[n + 1], consume[n + 1], vis[n + 1];
    
    while (true) {
        for (ll i = 1; i <= n; i++)dis[i] = flag;//初始化
        
        for (ll i = 1; i <= m; i++)
            if (a[i].be != a[i].en && a[i].va < dis[a[i].en])
                pre[a[i].en] = a[i].be, dis[a[i].en] = a[i].va;
                //找到每个点除了根节点外每个点被指向的最短距离的线,记录下来
                
        for (ll i = 1; i <= n; i++) {
            if (i != root && dis[i] == flag)return -1;
            vis[i] = consume[i] = 0;

            //查看是否有点没有指向的边,没有跳出即可
            //并完成初始化
        }
        
        ll cnt = 0;
        for (ll i = 1; i <= n; i++) {
            if (i == root)continue;
            sum += dis[i];//记录长度
			
            ll v = i;
            while (vis[v] != i && v != root && !consume[v])
                vis[v] = i, v = pre[v];
            //查看这个点最后是否还会等于自己本身,也就是判断环

            if (!consume[v] && v != root) {
                //已经和根节点相连,那么就无需将其缩点
                //已经缩点预处理过的无需再进行缩点预处理
                consume[v] = ++cnt;
                for (ll u = pre[v]; u != v; u = pre[u])
                    consume[u] = cnt;
            }//为缩环做准备
            
        }
        if (!cnt)return sum;
        
        for (ll i = 1; i <= n; i++)
            if (!consume[i])consume[i] = ++cnt;
            
        for (ll i = 1; i <= m; i++) {
            t = a[i].en;//记录一下未更新前的下标
            a[i].be = consume[a[i].be];
            a[i].en = consume[a[i].en];
            if (a[i].be != a[i].en)a[i].va -= dis[t];
            //当这个两个点不在一个环中的时候
            //最后已经确认为最佳路径的距离变成0,还未确认的点将距离缩一次
            //个人wa点在这里,dis里的下标是未更新前的,需要提前存储
            
            //更新的意义是在上次确认过最短路径,但不一定是最佳路径
            //因此,为了在求和的时候不会出现重复累加的情况
            //需要用指向此点可能最佳的路线长度减去最短路径长度
        }
        
        root = consume[root]; n = cnt;//更新一下被根节点以及总结点数
        
    }
}
int main() {
    FAST_IO;
    while (cin >> n >> m >> k) {
        for (i = flag = 1; i <= m; i++)cin >> a[i].be >> a[i].en >> a[i].va, flag += a[i].va;
        cout << zhuliu(k) << endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GUESSERR

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

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

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

打赏作者

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

抵扣说明:

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

余额充值