模板 - 最小树形图(朱刘算法)

整理的算法模板合集: ACM模板


给定一个根的有向图的最小树形图

给定包含 n 个结点,m 条有向边的一个图。试求一棵以结点 r 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 r
为根的最小树形图,输出 −1。

#include<bits/stdc++.h>
using namespace std;
const int N=106,M=10006,inf=2e8;
int n,m,rt,t,cnt=0,id[N],pre[N],ine[N],vis[N];
struct line{
    
    int x,y,c;}q[M];
inline int read(){
    
    
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){
    
    if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T;
}
int zhuliu(){
    
    
    int ans=0;
    while(true){
    
    
         cnt=0;
         for(int i=1;i<=n;++i) ine[i]=inf,vis[i]=0,id[i]=0;//预处理
         for(int i=1;i<=m;++i) if(q[i].x!=q[i].y&&ine[q[i].y]>q[i].c) ine[q[i].y]=q[i].c,pre[q[i].y]=q[i].x;//每个点的最短边
         for(int i=1;i<=n;++i) if(i!=rt&&ine[i]==inf) return -1;//有点无最短边
         for(int i=1;i<=n;++i){
    
    
             if(i==rt) continue;
             ans+=ine[i],t=i;
             while(vis[t]!=i&&!id[t]&&t!=rt) vis[t]=i,t=pre[t];
             //能走到环的点或者换上的点停下
             if(!id[t]&&t!=rt){
    
    
                id[t]=++cnt; //将环上的点标记为新的环
                for(int o=pre[t];o!=t;o=pre[o]) id[o]=cnt;
             }
         }//找环
         if(!cnt) break;//无环结束
         for(int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
         for(int i=1;i<=m;++i){
    
    
             t=q[i].y,q[i].x=id[q[i].x],q[i].y=id[q[i].y];
             if(q[i].x!=q[i].y) q[i].c-=ine[t];
         }
         n=cnt,rt=id[rt];
         //去旧图,换新图
    }
    return ans;
}
int main(){
    
    
    n=read(),m=read(),rt=read();
    for(int i=1;i<=m;++i) q[i].x=read(),q[i].y=read(),q[i].c=read();
    printf("%d\n",zl());
    return 0;
} 

为给定根的树形图

求没有确定的根的树形图:建立一个超级根r,以它为根跑算法,只要将r向原图每个点连接一条权值大于原图中所有边的边权的边,这样选这些边肯定不划算,因此只会选择一条。

判断无解的方法

判无解的奇技淫巧:从小到大依次枚举每个点i,加入边 ( i , ( i + 1 ) % n + 1 , + ∞ ) (i,(i+1)\%n+1,+\infty) (i,(i+1)%n+1,+) ,这样如果你最后得到的答案为 + ∞ +\infty +,那么就无解了。

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/108568123