最小生成树、次小生成树

一、最小生成树

说到生成树首先要解释一下树,树是一个联通的无向无环图,多棵树的集合则被称为森林。

因此,树具有许多性质:

1.两点之间的路径是唯一的。

2.边数等于点数减一。

3.连接任意两点都会生成一个环。

对于一个无向联通图G的子图,如果它包含G的所有点,则它被称为G的生成树,而各边权和最小的生成树则被称为最小权重生成树,简称最小生成树。

对于最小生成树问题有两种算法,一种是kruskal算法,另一种是prime算法。

kruskal算法:

kruskal算法就是在得到一张图之后把图上所有边按边权从小到大排序,然后删掉所有的边,再按排完的顺序把边一条一条地加进去,加边的同时用并查集维护两点间的连通性,如果一条边的两个端点已经联通,则不将这条边加入到图中,直到加了n-1条边为止。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std; 
int n,x,y;
double l;
double s;
int f[100001];
int i=0;
int sum;
double total=0;
struct demacia
{
    int a;
    int b;
    double d;
};
demacia m[100001];
 
int cmp(demacia x,demacia y)
{
    return x.d<y.d;
}
int find(int x)
{
    if(f[x]==x) 
    {
        return x;
    }
    else
    {
        f[x]=find(f[x]);
        return f[x];
    } 
}
int main()
{
    scanf("%lf",&s);
    scanf("%d",&n);
    while(scanf("%d%d%lf",&x,&y,&l) != EOF)
    {
        m[i].a=x;
        m[i].b=y;
        m[i].d=l;
        i++;
    }
    sum=i;
    sort(m,m+sum,cmp);
    for(int k=1;k<=n;k++)
    {
        f[k]=k;
    }
    for(int j=0;j<sum;j++)
    {
        int fx=find(m[j].a);
        int fy=find(m[j].b);
        if(fx!=fy)
        {
            total=total+m[j].d;
            f[fy]=fx;
        }
    }
    if(total>s||sum<n-1)
    {
        printf("Impossible");
    }
    else
    {
        printf("Need %.2lf miles of cable",total);
    }
} 

prime算法:

prime算法实际上就是一个贪心;把图中的点分成未处理的和处理过的两部分,每一次找到联通处理过的和未处理的两部分的所有边,连接边权最小的一条,用并查集维护即可。

kruskal算法适用于稀疏图而prime算法适用于稠密图。

二、次小生成树

次小生成树分为非严格次小生成树和严格次小生成树;非严格次小生成树是可以和最小生成树权值和相等的,而严格次小生成树是严格大于最小生成树的。

非严格次小生成树是在最小生成树kruskal算法的基础上加上lca(最好用倍增lca),在kruskal之后将未被选取的边依此枚举对每条边的两个端点做lca找出路径上边权最大的边然后将它换掉,直到把所有未选取的边都枚举一遍就求出次小生成树了。

而lca要与处理出来g[i][j]表示以i为起点往上2^j步的祖先与i 之间最大的一条边的边权。

严格次小生成树就只需要再预处理出来次大边就行了,但它要考虑的情况就比较多了。

非严格次小生成树

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int next[100050];
int to[100050];
int val[100050];
int head[10001];
int f[10001][15];
int g[10001][15];
int d[10001];
int F[10001];
int tot=0;
int sum;
int num;
struct node
{
	int x;
	int y;
	int v;
}a[50001];
void add(int x,int y,int v)
{
	tot++;
	next[tot]=head[x];
	head[x]=tot;
	to[tot]=y;
	val[tot]=v;
}
int cmp(node a,node b)
{
	return a.v>b.v;
}
int find(int x)
{
	if(F[x]==x)
	{
		return x;
	}
	return F[x]=find(F[x]);
}
void dfs(int x,int fx)
{
	f[x][0]=fx;
	d[x]=d[fx]+1;
	for(int j=1;(1<<j)<=n;j++)
	{
		f[x][j]=f[f[x][j-1]][j-1];
		g[x][j]=min(g[f[x][j-1]][j-1],g[x][j-1]);
	}
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=fx)
		{
			g[to[i]][0]=val[i];
			dfs(to[i],x);
		}
	}
}
int lca(int x,int y)
{
	int total=0;
	if(d[x]<d[y])
	{
		swap(x,y);
	}
	int deep=d[x]-d[y];
	for(int i=0;i<=14;i++)
	{
		if((deep&(1<<i))!=0)
		{
			total=max(total,g[x][i]);
			x=f[x][i];
		}
	}
	if(x==y)
	{
		return total;
	}
	for(int j=14;j>=0;j--)
	{
		if(f[x][j]!=f[y][j])
		{
			total=max(total,max(g[x][j],g[y][j]));
			x=f[x][j];
			y=f[y][j];
		}
	}
	return total=max(max(g[x][0],g[y][0]),total);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		F[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
	}
	sort(a+1,a+1+m,cmp);
	num=0;
	sum=0;
	for(int j=1;j<=m;j++)
	{
		int fx=find(a[j].x);
		int fy=find(a[j].y);
		if(fx!=fy)
		{
			F[fx]=fy;
			num++;
			sum+=a[j].v;
			add(a[j].x,a[j].y,a[j].v);
			add(a[j].y,a[j].x,a[j].v);
		}
	}
	g[1][0]=0;
	dfs(1,1);
	int q;
	scanf("%d",&q);
	int A,B;
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&A,&B);
		printf("%d\n",lca(A,B));
	}
}
严格次小生成树
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int next[600001];
int to[600001];
int val[600001];
int head[100001];
int f[100001][20];
int g[100001][20];
int g2[100001][20];
int d[100001];
int F[100001];
int tot=0;
long long sum;
long long num;
long long cnt;
long long ans=1000000000000000ll;
struct node
{
    int x;
    int y;
    int v;
}a[300001];
struct node1
{
    int x;
    int y;
    int v;
}b[300001];
void add(int x,int y,int v)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    val[tot]=v;
}
int cmp(node a,node b)
{
    return a.v<b.v;
}
int find(int x)
{
    if(F[x]==x)
    {
        return x;
    }
    return F[x]=find(F[x]);
}
void dfs(int x,int fx)
{
    f[x][0]=fx;
    d[x]=d[fx]+1;
    for(int j=1;j<=19;j++)
    {
        f[x][j]=f[f[x][j-1]][j-1];
        if(g[x][j-1]>g[f[x][j-1]][j-1])
        {
            g[x][j]=g[x][j-1];
            g2[x][j]=max(g[f[x][j-1]][j-1],g2[x][j-1]);
        }
        else if(g[x][j-1]<g[f[x][j-1]][j-1])
        {
            g[x][j]=g[f[x][j-1]][j-1];
            g2[x][j]=max(g[x][j-1],g2[f[x][j-1]][j-1]);
        }
        else
        {
            g[x][j]=g[f[x][j-1]][j-1];
            g2[x][j]=max(g2[x][j-1],g2[f[x][j-1]][j-1]);
        }
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fx)
        {
            g[to[i]][0]=val[i];
            dfs(to[i],x);
        }
    }
}
int lca(int x,int y)
{
    int total=0;
    if(d[x]<d[y])
    {
        swap(x,y);
    }
    int deep=d[x]-d[y];
    for(int i=0;i<=19;i++)
    {
        if((deep&(1<<i))!=0)
        {
            total=max(total,g[x][i]);
            x=f[x][i];
        }
    }
    if(x==y)
    {
        return total;
    }
    for(int j=19;j>=0;j--)
    {
        if(f[x][j]!=f[y][j])
        {
            total=max(total,max(g[x][j],g[y][j]));
            x=f[x][j];
            y=f[y][j];
        }
    }
    return total=max(max(g[x][0],g[y][0]),total);
}
int lca2(int x,int y,int z)
{
    int total=0;
    if(d[x]<d[y])
    {
        swap(x,y);
    }
    int deep=d[x]-d[y];
    for(int i=0;i<=19;i++)
    {
        if((deep&(1<<i))!=0)
        {
            if(g[x][i]!=z)
            {
                total=max(g[x][i],total);
            }
            else
            {
                total=max(g2[x][i],total);
            }
            x=f[x][i];
        }
    }
    if(x==y)
    {
        return total;
    }
    for(int j=19;j>=0;j--)
    {
        if(f[x][j]!=f[y][j])
        {
            if(g[x][j]!=z)
            {
                total=max(total,g[x][j]);
            }
            else
            {
                total=max(total,g2[x][j]);
            }
            if(g[y][j]!=z)
            {
                total=max(g[y][j],total);
            }
            else
            {
                total=max(g2[y][j],total);
            }
            x=f[x][j];
            y=f[y][j];
        }
    }
    if(g[x][0]!=z)
    {
        total=max(total,g[x][0]);
    }
    else
    {
        total=max(total,g2[x][0]);
    }
    if(g[y][0]!=z)
    {
        total=max(total,g[y][0]);
    }
    else
    {
        total=max(total,g2[y][0]);
    }
    return total;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        F[i]=i;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
    }
    sort(a+1,a+1+m,cmp);
    num=0;
    sum=0;
    cnt=0;
    int j=1;
    for(int j=1;j<=m;j++)
    {
        int fx=find(a[j].x);
        int fy=find(a[j].y);
        if(fx!=fy)
        {
            F[fx]=fy;
            num++;
            sum+=a[j].v;
            add(a[j].x,a[j].y,a[j].v);
            add(a[j].y,a[j].x,a[j].v);
        }
        else
        {
            cnt++;
            b[cnt].x=a[j].x;
            b[cnt].y=a[j].y;
            b[cnt].v=a[j].v;
        }
    }
    g[1][0]=0;
    dfs(1,1);
    for(int i=1;i<=cnt;i++)
    {
        int z=lca(b[i].x,b[i].y);
        int k=b[i].v-z;
        if(k==0)
        {
            k=b[i].v-lca2(b[i].x,b[i].y,z);
        }
        if(ans>k)
        {
            ans=k;
        }
    }
    long long gg=sum+ans;
    printf("%lld",gg);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值