最小生成树邻接表模板

/*我的经验是prim加了堆优化之后比kruskal快一点点点点点点。所以99%的时间都是用的kruskal*/
/*prim算法(矩阵形式):*/

#include<stdio.h>
#include<algorithm>
#define M 6000*3  //双向边 所以要乘 切记
#define inf 0x3f3f3f3f

int e;//边数  
//prim算法(边表形式):

struct Edge//frm为起点,to为终点,w为边权,nxt指向下一个顶点
{
    int frm;
    int to,w,nxt;
}edge[M];

int vis[M],head[M],dis[M];
void addedge (int cu,int cv,int cw)//生成边的函数
{
    edge[e].frm = cu;
    edge[e].to = cv;
    edge[e].w = cw;
    edge[e].nxt = head[cu];//前一个.frm=cu的edge编号,如果没有 head[cu]=-1;
    head[cu] = e ++;//head[cu] 存最后一次存入的edge 编号
    
	edge[e].frm = cv;
    edge[e].to = cu;
    edge[e].w = cw;
    edge[e].nxt = head[cv];
    head[cv] = e ++;
}

int prim(int n,int sta) //n为顶点数量,sta为起点
{
    if(e/2<n-1)
		return -1; //边不够 返回-1 表示不完全图
    int sum = 0,i,flag;
    for(i=0;i<n;i++)
    {
	   dis[i]=inf;
    }
    memset(vis,0,sizeof(vis));
    for (i = head[sta];i != -1;i = edge[i].nxt)//遍历与sta点相连的所有顶点
    {
        int v = edge[i].to;
        dis[v] = edge[i].w;
    }
    vis[sta] = 1; //加入到最小生成树中
    int m = n - 1;  //只生成n-1条边,所以循环n-1次
	
    while (m --)
    {
	if(n==0)
		return-1
	if(e/2<n-1)
		return -1;
        int min = inf;
        for (i = 0;i < n;i ++)/出当前边权最小的边
		{
			if (!vis[i]&&dis[i] < min)
                flag = i,min = dis[i];
		}
		if(min==inf) //没找到可以用的边  代表图不完全 返回-1
			return -1;
        sum += dis[flag];
        vis[flag] = 1;//加入到最小生成树中
        for (i = head[flag];i != -1;i = edge[i].nxt)//更新与flag顶点相连的点的dis
        {                                           //
            int v = edge[i].to;
            if (edge[i].w < dis[v])
                dis[v] = edge[i].w;
        }
    }
    return sum; //返回边权总和
}

int main () 
{
	int a,b,w,n,i,j;
	while(scanf("%d",&n),n)
	{
		memset (head,-1,sizeof(head));
		e = 0;                  //记得初始化
		for(i=0;i<n*(n-1)/2;i++)
		{
				scanf ("%d%d%d",&a,&b,&w);
				a--,b--;
				addedge(a,b,w);
		}
		
		printf("%d\n",prim(n,1));
		//prim(int n,int sta) //n为顶点数量,sta为起点
	}
	return 0;
}



/*
邻接矩阵
int prim(int n,int sta)//n表示有n个顶点,sta表从sta这个顶点出发生成最小生成树
{
    int mark[M],dis[M],flag;
    int i,sum = 0,j;     //sum是总的最小生成树边权值
    for (i = 0;i < n;i ++) //初始化dis[i] 表从顶点sta到点i的权值
    {
        dis[i] = mat[sta][i];
        mark[i] = 0;
    }
    mark[sta] = 1;           //sta 这个顶点加入最小生成树中
    for (i = 1;i < n;i ++)   //循环n-1次,每次找出一条最小权值的边 n个点的图
    {                        //只有n-1条边
        int min = inf;       //inf 表无穷大
        for (j = 0;j < n;j ++)/出当前未在最小生成树中边权最小的顶点
            if (!mark[j] && dis[j] < min)
                min = dis[j],flag = j;
        mark[flag] = 1;         //把该顶点加入最小生成树中
        sum += dis[flag];       //sum加上其边权值
        for (j = 0;j < n;j ++)  //以falg为起点更新到各点是最小权值
            if (dis[j] > mat[flag][j])
                dis[j] = mat[flag][j];
    }
    return sum;       //返回边权总和
}
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值