【图论】朱刘算法求最小树形图

以洛谷P4716为例(有定根)

#include<bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int M=1e6+5;
const int N=1e6+5;
const int inf=2e9;
struct Edge
{
    int u,v,w;
} e[M];

int n,m,root;
int pre[N],ine[N];
int vis[N],id[N];
int zhuliu(int root)
{
    int ans=0;
    while(1)
    {
        for(int i=1;i<=n;i++) ine[i]=inf;  // 初始化 
        for(int i=1;i<=m;i++)
        {
            int u=e[i].u,v=e[i].v;
            if(u!=v&&e[i].w<ine[v])  // 遍历所有边,对每个点找最小的入边 
                ine[v]=e[i].w,pre[v]=u;
        }
        for(int i=1;i<=n;i++)  //判定无解 
            if(i!=root&&ine[i]==inf) return -1;
        int cnt=0;
        for(int i=1;i<=n;i++) vis[i]=id[i]=0;
        for(int i=1;i<=n;i++)
        {
            if(i==root) continue;
            ans+=ine[i];
            int v=i;
            while(vis[v]!=i&&!id[v]&&v!=root)  // 找环 
            {
                vis[v]=i;
                v=pre[v];
            }
            if(!id[v]&&v!=root)
            {
                id[v]=++cnt;  // 把环上的点标记为同一点 
                for(int u=pre[v]; u!=v; u=pre[u])
                    id[u]=cnt;
            }
        }
        if(cnt==0) break; // 无环,得到解 
        for(int i=1;i<=n;i++)
            if(!id[i]) id[i]=++cnt;
        for(int i=1;i<=m;i++)
        {
            int u=e[i].u,v=e[i].v;
            e[i].u=id[u],e[i].v=id[v];
            if(id[u]!=id[v]) e[i].w-=ine[v]; //修改边权 
        }
        root=id[root];
        n=cnt;
    }
    return ans;
}

int main(){
	cin>>n>>m>>root;
	for(int i=1;i<=m;i++){
		cin>>e[i].u>>e[i].v>>e[i].w;
	}
	cout<<zhuliu(root)<<endl;
}

hdu2121无定根的:

#include<bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int M=1e5+1e4+5;
const int N=1e3+5;
const ll inf=1e17;
struct Edge
{
    int u,v,w;
} e[M];

int n,m,root,minroot;
int pre[N];ll ine[N];
int vis[N],id[N];
ll zhuliu(int root)
{
    ll ans=0;
 
    while(1)
    {
        for(int i=0;i<=n;i++) ine[i]=inf; 
        for(int i=1;i<=m;i++)
        {
            int u=e[i].u,v=e[i].v;
            if(u!=v&&e[i].w<ine[v]){
                ine[v]=e[i].w,pre[v]=u;
				if(u==root) minroot=i;
			}	
        }
        for(int i=0;i<=n;i++) 
            if(i!=root&&ine[i]==inf) return -1;
        int cnt=0;
        for(int i=0;i<=n;i++) vis[i]=id[i]=-1;
        ine[root]=0;
        for(int i=0;i<=n;i++)
        {
            if(i==root) continue;
            ans+=ine[i];
            int v=i;
            while(vis[v]!=i&&id[v]==-1&&v!=root)  
            {
                vis[v]=i;
                v=pre[v];
            }
            if(id[v]==-1&&v!=root)
            {
                for(int u=pre[v]; u!=v; u=pre[u]){
                    id[u]=cnt;
				}
                id[v]=cnt++;                    
            }
        }
        if(cnt==0) break; 
        for(int i=0;i<=n;i++)
            if(id[i]==-1) id[i]=cnt++;
        for(int i=1;i<=m;i++)
        {
            int u=e[i].u,v=e[i].v;
            e[i].u=id[u],e[i].v=id[v];
            if(id[u]!=id[v]) e[i].w-=ine[v];
        }
        root=id[root];
        n=cnt-1;
    }
    return ans;
}

int main(){
	while(cin>>n>>m){
		int mm=m;m=m+n;ll SUM=0;
		for(int i=1;i<=mm;i++){
			int u,v,w;cin>>u>>v>>w;u+=1,v+=1;
			e[i].u=u,e[i].v=v,e[i].w=w;SUM+=w;
		}
		SUM++;
		for(int i=mm+1;i<=m;i++){
			e[i].u=0,e[i].v=i-mm,e[i].w=SUM;
		}
		ll res=zhuliu(0);
		if(res>=2*SUM||res==-1) cout<<"impossible"<<endl<<endl;
		else{
			cout<<res-SUM<<" "<<minroot-1-mm<<endl<<endl;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值