hdu6141-多校8&最小树形图&朱刘算法-I am your Father!

本文介绍了一种求解有向图最小生成树问题的算法——朱刘算法,并通过一道具体题目展示了算法的应用过程。该算法首先选择最小权重边,然后处理环路直至不再存在环路,最终实现有向图的最小生成树构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://acm.hdu.edu.cn/showproblem.php?pid=6141
比赛的时候根本没看那道题。
学习中。mark一下。
最小树形图 就是有向图的最小生成树。
一般用朱刘算法。
1 加入最小的边。如果存在孤立点,那么肯定不会有最小树形图。break
2 如果有 不包含root的环,对环进行缩点。
3 反复进行,直到没有环。
这里写图片描述
但这一道题还有不一样的地方,因为要求最大,所以全变成负数。
要 v-1这个点 的父节点尽可能小,所以 v-1的邻接边 全加上他的 父节点,
最后在后面的处理。。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))


const int MAXV=1000+3;//最大顶点数
const int MAXE=10000+3;//最大边数
struct Edge
{
    int u, v;
    int cost;
}edge[MAXE];//边集
int V,E;//顶点数,边数
int pre[MAXV],id[MAXV],vis[MAXV];
int in[MAXV];
int zhuliu(int root)//返回最小花费
{
    int res=0;
    while(true)
    {
        for(int i=0;i<V;++i)
            in[i]=INF;
        for(int i=0;i<E;++i)
            if(edge[i].u!=edge[i].v&&edge[i].cost<in[edge[i].v])
            {
                pre[edge[i].v]=edge[i].u;
                in[edge[i].v]=edge[i].cost;//在所有点中寻找最小的。标定他的 弧头
            }
        for(int i=0;i<V;++i)
            if(i!=root&&in[i]==INF)
                return -1;//不存在最小树形图,因为有一个 孤立的点,怎么连都连不到
        int tn=0;//新图结点数
        for(int i=0;i<V;++i)
        {
            id[i]=-1;
            vis[i]=-1;
        }
        in[root]=0;
        for(int i=0;i<V;++i)
        {
            res+=in[i];
            int v=i;
            while(vis[v]!=i&&id[v]==-1&&v!=root)
            {
                vis[v]=i;
                v=pre[v];// 向后遍历。vis是记录的 那条链的尾
            }
            if(v!=root&&id[v]==-1)
            {
                for(int u=pre[v];u!=v;u=pre[u])
                    id[u]=tn;
                id[v]=tn++;//求环、id是记录环的标号。缩点操作
            }
        }
        if(tn==0)//没有有向环
            break;
        for(int i=0;i<V;++i)
            if(id[i]==-1)
                id[i]=tn++;// 自环
        for(int i=0;i<E;i++)
        {
            int v=edge[i].v;
            edge[i].u=id[edge[i].u];
            edge[i].v=id[edge[i].v];
            if(edge[i].u!=edge[i].v)
                edge[i].cost-=in[v];
            else swap(edge[i],edge[--E]);
        }
        V=tn;
        root=id[root];
    }
    return res;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--){
          scanf("%d%d",&V,&E);
          for(int i=0;i<E;i++){
              scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].cost);
              edge[i].u--,edge[i].v--;
              edge[i].cost*=-1000;
               if(edge[i].v==V-1)
                   edge[i].cost+=edge[i].u;
          }
          int ans=zhuliu(0);
          printf("%d %d\n",(-ans+999)/1000,(-ans+999)/1000*1000+ans+1);//最后加1,因为过程中处理的是 0-v-1的,所以要加1变为原来的、
    }
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值