hdu2121(不确定起始点的最小生成图,模板,学习)

题意:要你在一些城市中选一个做首都 , 要求首都都能到其他城市 , 道路花费要最少 , 且道路都是单向的 , 这个时候就要用到最小树形图算法了 , 而且是不固定根.

解决的问题:最小生成图就是要求从某个原点能到其他所有点,且构成这颗树的边权和最小。

算法复杂度O(VE)

关于算法实现步骤详见https://www.cnblogs.com/xzxl/p/7243466.html

#include<bits/stdc++.h>
using namespace std;  
typedef double type;  
#define INF 2000000000  
#define N 1005  
#define ll long long
struct edge //图的结构体  
{  
    int u,v;
	ll w;  
}e[10005];  
int m,n,pre[N],id[N],visit[N],xroot;  
ll in[N],sum;  
//eCnt为图中的边数  
//n为图中的顶点数  
//pre[i]为顶点i的前驱节点  
//id[i]为缩环,形成新图的中间量  
//in[i]为点i的最小入边  
//visit[i]遍历图时记录顶点是否被访问过  
ll directedMST(int root,int nv,int ne)  
{  
    ll ans=0;  
    while(1)  
    {  
        //1.找最小入边  
        for(int i=0;i<nv;i++) in[i]=INF;  
        for(int i=0;i<ne;i++)  
        {  
            int u=e[i].u;  
            int v=e[i].v;  
            if(u!=v&&e[i].w<in[v])  
            {  
                if(u==root)  //此处标记与源点相连的最小边  
                    xroot=i;  
                in[v]=e[i].w;  
                pre[v]=u;  
            }  
        }  
        for(int i=0;i<nv;i++)                          //判断图是否连通  
            if(i!=root&&in[i]==INF) return -1;  //除了跟以外有点没有入边,则根无法到达它  
         //2.找环  
        int nodeCnt=0;          //图中环的数目  
        memset(id, -1, sizeof(id));  
        memset(visit, -1, sizeof(visit));  
        in[root]=0;  
        for(int i=0;i<nv;i++)  
        {  
            ans+=in[i];  
            int v=i;  
            while(visit[v]!=i&&id[v]==-1&&v!=root)//每个点寻找其前序点,要么最终寻找至根部,要么找到一个环  
            {  
                visit[v]=i;  
                v=pre[v];  
            }  
            if(v!=root&&id[v]==-1)//缩点  
            {  
                for(int u=pre[v];u!=v;u=pre[u])  
                    id[u]=nodeCnt;  
                id[v]=nodeCnt++;  
            }  
        }  
        if(nodeCnt==0) break;//如果无环,跳出循环  
        for(int i=0; i<nv; i++)  
            if(id[i]==-1)  
                id[i]=nodeCnt++;  
        //3.缩点,重新标记  
        for(int i=0;i<ne;i++)  
        {  
            int v=e[i].v;  
            e[i].u=id[e[i].u];  
            e[i].v=id[e[i].v];  
            if(e[i].u!=e[i].v)  
                e[i].w-=in[v];  
        }  
        nv=nodeCnt;  
        root=id[root];  
    }  
    return ans;  
}  
  
int main()  
{  
	//freopen("t.txt","r",stdin);
    int m;  
    while(scanf("%d%d",&n,&m)!=EOF)  
    {  
        sum=0;  
        for(int i=0;i<m;i++)  
        {  
            scanf("%d%d%lld",&e[i].u,&e[i].v,&e[i].w);  
            e[i].u++;e[i].v++;     //都++之后,把0设为超级源点,联通各点  
            sum+=e[i].w;  
            if(e[i].u==e[i].v)  
                e[i].w=INF;//消除自环  
        }  
        sum++;      //此处必须++,因为需要权值比总权值大,因为这个w几次,,,  
        for(int i=m;i<n+m;i++)  
        {  
            e[i].u=0;  
            e[i].v=i-m+1;  
            e[i].w=sum;  
        }  
        ll ans=directedMST(0,n+1,m+n);  
        if(ans==-1 || ans-sum>=sum) printf("impossible\n");//ans-sum是除去虚根的最小树形图的最短路径,如果这个距离比所有的边权值和sum还大,说明还有另外的边由虚点发出,故说明此图不连通  
        else printf("%lld %d\n", ans-sum, xroot-m);  
        printf("\n");  
    }  
    return 0;  
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值