最小树形图

朱刘算法

算法

1、对于每个点,选择它入度最小的那条边
2、如果形成了最小环,那么就缩环成点,并且更新其他点到环的距离。如果没有环,结束

实现
1、 a n s = ∑ i = 1 n d i s [ i ] ans=\sum_{i=1}^n dis[i] ans=i=1ndis[i] 首先就选择了 n-1 条边,得到了最小的答案,然后这个答案只是贪心选择的结果,可能选到了环,所以需要修正

练习

P4716 【模板】最小树形图

链接:https://www.luogu.com.cn/problem/P4716

题意:求指定根节点的最小树形图

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100+5,maxm=1e4+5,inf=2e9;

struct Edge
{
	int u,v,w;
}edges[maxm];

int dis[maxn],id[maxn],visit[maxn],pre[maxn];

int zhuliu(int n,int m,int rt)
{
	int ans=0;
	while(1)
	{
		for(int i=1;i<=n;++i) dis[i]=inf,id[i]=visit[i]=0;
		for(int i=1;i<=m;++i)
		{
			int u=edges[i].u,v=edges[i].v,w=edges[i].w;
			if(u!=v&&w<dis[v]) pre[v]=u,dis[v]=w;
		}
		for(int i=1;i<=n;++i)
			if(i!=rt&&dis[i]==inf) return -1;	
		int cnt=0;	
		for(int i=1;i<=n;++i)
		{
			if(i==rt) continue;
			ans+=dis[i];
			int v=i;
			while(visit[v]!=i&&!id[v]&&v!=rt)
				visit[v]=i,v=pre[v];
			if(!id[v]&&v!=rt)
			{
				id[v]=++cnt;
				for(int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
			}
		}
		if(cnt==0) return ans;
		for(int i=1;i<=n;++i)
			if(!id[i]) id[i]=++cnt;
		for(int i=1;i<=m;++i)
		{
			int u=edges[i].u,v=edges[i].v;
			edges[i].u=id[u],edges[i].v=id[v];
			if(id[u]!=id[v]) edges[i].w-=dis[v];
			//对于环的入边会大于等于0,其他的边就会一直都等于0
		}
		rt=id[rt],n=cnt;
	}
}
int n,m,rt;

int main()
{
	scanf("%d%d%d",&n,&m,&rt);
	for(int i=1;i<=m;++i)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		edges[i]={u,v,w};
	}
	printf("%d\n",zhuliu(n,m,rt));
	return 0;
}

Ice_cream’s world II POJ - 2121

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121

题意:求不定根的最小树形图,多解时输出最小的根

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e3+5,maxm=1e4+5,inf=2e9;

struct Edge
{
    int u,v,w;
} edges[maxm];

int dis[maxn],id[maxn],visit[maxn],pre[maxn];
int pos;
int zhuliu(int n,int m,int rt)
{
    int ans=0;
    int Case=0;
    while(1)
    {
        ++Case;
        for(int i=1; i<=n; ++i) dis[i]=inf,id[i]=visit[i]=0;
        for(int i=1; i<=m; ++i)
        {
            int u=edges[i].u,v=edges[i].v,w=edges[i].w;
            if(u!=v&&w<dis[v])
            {
                pre[v]=u,dis[v]=w;
                if(u==rt) pos=i;
            }
        }
        for(int i=1; i<=n; ++i)
            if(i!=rt&&dis[i]==inf) return -1;
        int cnt=0;
        for(int i=1; i<=n; ++i)
        {
            if(i==rt) continue;
            ans+=dis[i];
            int v=i;
            //id==0表示不属于某个环,并且
            while(visit[v]!=i&&!id[v]&&v!=rt)
                visit[v]=i,v=pre[v];
            if(!id[v]&&v!=rt)
            {
                id[v]=++cnt;
                for(int u=pre[v]; u!=v; u=pre[u]) id[u]=cnt;
            }
        }
        if(cnt==0) return ans;
        for(int i=1; i<=n; ++i)
            if(!id[i]) id[i]=++cnt;
        for(int i=1; i<=m; ++i)
        {
            int u=edges[i].u,v=edges[i].v;
            edges[i].u=id[u],edges[i].v=id[v];
            if(id[u]!=id[v]) edges[i].w-=dis[v];
        }
        rt=id[rt],n=cnt;
    }
}
int n,m,rt;

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        ll sum=0;
        for(int i=1; i<=m; ++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u++,v++;
            edges[i]= {u,v,w};
            sum+=w;
        }
        for(int i=1; i<=n; ++i) edges[i+m]= {n+1,i,sum+1};
        ll ans=zhuliu(n+1,n+m,n+1);
        if(ans==-1||ans>2*sum+1) puts("impossible\n");
        else printf("%lld %d\n\n",ans-sum-1,pos-m-1);
    }
    return 0;
}
/*
3 3
0 1 1
1 2 2
2 0 3
3 3
0 1 2
1 2 2
2 0 2
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值