朱(qiu)刘算法一些需要注意的地方

今天上午考的模拟赛出了朱(qiu)刘算法的裸题,什么都不会我的现场学了一发,虽然还不是很理解 还是写下一些心得。


1.朱刘算法是一种解决最小树形图的算法,与最小生成树类比的话,最小树形图是对于有向图的问题,一般是对于一个给定的根节点,在有向图中选定一个边集,使得边的权值和最小并且根节点可以到达所有节点。
2.算法主要过程如下,首先对于所有点贪心选取一个连向它的边中找到边权最小的一条加入当前的图,如果除了root以外还有其他节点入度为0,那么就不存在最小树形图。然后对于当前图中,如果没环此时就是最小树形图,否则缩点后重新标号重复以上操作,总的复杂度是 O(nm) O ( n m ) 的。
3.一些需要注意的地方 如果不确定root,那么新定义一个点往所有点都连一条无穷大的边,那么权值和便是ans - inf ,并且如果ans - inf > inf 那么代表无穷大这条边不止被调用了一次,所以此时没有最小树形图。 确定每个点的 前驱连向它的边的最小值时 一定要判断自环的存在!! 否则将会死循环 找环的时候往前驱暴力找,如果跳到了根节点或者跳到了另一个环上那么就说明不在环上,如果跳到了一个访问过的节点,就从这个节点开始倒着找一圈标记为一个环。
4.一个特别重要的地方就是有关重新编号,对于一条边(u,v,w)有三种情况,一是它连接着两个点 那么他已经被计入答案或者比他更小的一条(u,v,w1)已经被记入答案,那么这条边的权值减去in[v]也就是连向v点最小的边权,二是他连接在环内,那么缩点以后这成为了一个自环不会对后面产生影响,三是他连接在一个点与一个环或是两个环之间之间,那么我们如果选择了这条边,那么环内指向点v的更小边已经被统计进了答案,所以我们应该减去这部分影响,即对当前边边权减去in[v]。

例题 uva11183

#include<bits/stdc++.h>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define inf (0x3f3f3f3f)

using namespace std;

const int maxn = 1e3 + 3, maxm = 4e4 + 3;
int vis[maxn], id[maxn], in[maxn], pre[maxn];
int T, n, m;

struct node
{
    int x, y, z;
}edge[maxm];

template<class T>inline bool chkmin(T &_, T __)
{
    return _ > __ ? _ = __, 1 : 0;
}

int zhuliu(int n, int m, int root)
{
    int res = 0, cnt;
    while(true)
    {
        For(i, 1, n)
            in[i] = inf, vis[i] = id[i] = 0;
        For(i, 1, m)
            if(edge[i].x != edge[i].y && chkmin(in[edge[i].y], edge[i].z))
                pre[edge[i].y] = edge[i].x;
        For(i, 1, n)
            if(i != root && in[i] == inf)
                return -1;
        in[root] = cnt = 0;
        For(i, 1, n)
        {
            res += in[i];
            int now = i;
            while(!id[now] && now != root && vis[now] != i)
            {
                vis[now] = i;
                now = pre[now];
            }
            if(now != root && !id[now])
            {
                id[now] = ++ cnt;
                for(int x = pre[now]; x != now; x = pre[x])
                    id[x] = cnt;
            }
        }
        if(cnt == 0)
            return res;
        For(i, 1, n)
            if(!id[i])
                id[i] = ++ cnt;
        For(i, 1, m)
        {
            int v = edge[i].y;
            edge[i].x = id[edge[i].x];
            edge[i].y = id[edge[i].y];
            edge[i].z -= in[v];
        }
        root = id[root];
        n = cnt;
    }
}

int main()
{
    freopen("uva11183.in", "r", stdin);
    freopen("uva11183.out", "w", stdout);
    cin >> T;
    For(cas, 1, T)
    {
        scanf("%d%d", &n, &m);
        For(i, 1, m)
        {
            scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].z);
            edge[i].x ++ , edge[i].y ++ ;
        }
        int ans = zhuliu(n, m, 1);
        if(ans == -1)
            printf("Case #%d: Possums!\n", cas);
        else
            printf("Case #%d: %d\n", cas, ans);
    }
    return 0;
}

其实我也不知道这个算法除了做这个还可以干嘛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值