• P4716 【模板】最小树形图


    题意

    说一下我对朱刘算法的理解:

    首先我们考虑树形图的性质:每个点除了根节以外都含有一条入边。

    因此我们可以有一个贪心的想法:对每个点(除了根节点)找到一条最短的入边,但是这样会出现环,如下图:

    我们会找到(2-3-4)这个环。

    根据贪心的思想,我们最终的答案必定含有这个环去掉一条边,于是我们将这三个点缩成一个点,加上这三条边的答案,并且修改所有连向这三个点的边的边权。

    举个例子:
    原来有条边(1->4),边权为(4),连向(4)的最小边权为(2),我们已经加上了(2),因此如果再选(1->4),那么应该再加上(4-2=2),于是这条边的边权改为(2)

    我们不断迭代,复杂度为(O(nm))

    code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=110;
    const int maxm=1e4+10;
    const int inf=1e9;
    int n,m,root;
    int fa[maxn],pre[maxn],mindis[maxn],col[maxn];
    struct Edge{int u,v,w;}E[maxm];
    inline ll solve()
    {
    	ll res=0;
    	while(2333)
    	{
    		int cnt=0;
    		for(int i=1;i<=n;i++)fa[i]=pre[i]=col[i]=0,mindis[i]=inf;
    		for(int i=1;i<=m;i++)
    			if(E[i].u!=E[i].v&&mindis[E[i].v]>E[i].w)
    				pre[E[i].v]=E[i].u,mindis[E[i].v]=E[i].w;
    		mindis[root]=0;
    		for(int i=1;i<=n;i++)
    		{
    			if(mindis[i]==inf)return -1;
    			res+=mindis[i];
    			int x=i;
    			while(x!=root&&fa[x]!=i&&!col[x])fa[x]=i,x=pre[x];
    			if(x!=root&&!col[x])
    			{
    				col[x]=++cnt;
    				int y=pre[x];
    				while(y!=x)col[y]=cnt,y=pre[y];
    			}
    		}
    		if(!cnt)return res;
    		for(int i=1;i<=n;i++)if(!col[i])col[i]=++cnt;
    		for(int i=1;i<=m;i++)
    		{
    			int delta=mindis[E[i].v];
    			E[i].u=col[E[i].u],E[i].v=col[E[i].v];
    			if(E[i].u!=E[i].v)E[i].w-=delta;
    		}
    		n=cnt;root=col[root];
    	}
    	return 233;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&root);
    	for(int i=1;i<=m;i++)scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
    	printf("%lld",solve());
    	return 0;
    } 
    
  • 相关阅读:
    JS使用 popstate 事件监听物理返回键
    JQ判断div是否隐藏
    SQL Server DATEDIFF() 函数
    取消a或input标签聚焦后出现虚线框
    C#定时任务
    C# 保留N位小数
    C#打印单据
    SQL语句创建函数
    SVN检出新项目
    解决jQuery的toggle()的自动触发问题
  • 原文地址:https://www.cnblogs.com/nofind/p/12092103.html
Copyright © 2020-2023  润新知