最小生成树

题目链接

P3366 【模板】最小生成树


【模板】最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。

输入格式

第一行包含两个整数 \(N,M\),表示该图共有 \(N\) 个结点和 \(M\) 条无向边。

接下来 \(M\) 行每行包含三个整数 \(X_i,Y_i,Z_i\) ,表示有一条长度为 \(Z_i\) 的无向边连接结点 \(X_i,Y_i\)

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。

输入

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

输出

7

说明/提示

数据规模:

对于 \(20\%\) 的数据,\(N\le 5,M\le 20\)

对于 \(40\%\) 的数据,\(N\le 50,M\le 2500\)

对于 \(70\%\) 的数据,\(N\le 500,M\le 10^4\)

对于 \(100\%\) 的数据:\(\le N\le 5000,1\le M\le 2\times 10^5 ,1\le Z_i \le 10^4\)

prim

  • 时间复杂度:\(O(n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5005,inf=0x3f3f3f3f;
int a[N][N];
int d[N];
bool v[N];
int n,m;
int prim()
{
    memset(d,0x3f,sizeof d);
    int res=0;
    d[1]=0;
    for(int i=1;i<=n;i++)
    {
        int x=0;
        for(int j=1;j<=n;j++)
            if(!v[j]&&(x==0||d[j]<d[x]))x=j;
        if(d[x]==inf)return inf;
        v[x]=true;
        res+=d[x];
        for(int j=1;j<=n;j++)
            d[j]=min(d[j],a[x][j]);
    }
    return res;
}
int main()
{
    memset(a,0x3f,sizeof a);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)a[i][i]=0;
    while(m--)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[x][y]=a[y][x]=min(a[x][y],z);
    }
    int res=prim();
    if(res==inf)
        puts("orz");
    else
        printf("%d",res);
    return 0;
}

kruskal

  • 时间复杂度:\(O(mlogm)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5005,M=2e5+10;
struct rec
{
    int x,y,z;
    bool operator<(rec &other)
    {
        return z<other.z;
    }
}edge[M];
int fa[N];
int n,m,s,cnt;
int res;
bool v[N];
int get(int x)
{
    if(x==fa[x])return x;
    return fa[x]=get(fa[x]);
}
void kruskal()
{
    sort(edge,edge+m);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=0;i<m;i++)
    {
        int x=get(edge[i].x),y=get(edge[i].y),w=edge[i].z;
        if(x==y)continue;
        cnt++;
        res+=w;
        fa[x]=y;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
        v[edge[i].x]=v[edge[i].y]=true;
    }
    kruskal();
    if(cnt==n-1)
        printf("%d",res);
    else
        puts("orz");
    return 0;
}

Borůvka

相当于多源 \(prim\) 算法,即多个连通块同时扩展连向其他连通块的最小边,由于是所有连通块同时进行的,所以每次连通块的合并会大概有一半的连通块减少,即整个连通块的合并操作有 \(O(logn)\) 次,同时注意可能会有两个连通块选择了同一条边,需要标记防止重边,同时还可能出现环的情况,合并时判断是否在一个连通块内即可

  • 时间复杂度:\(O(mlogn)\)

代码

// Problem: P3366 【模板】最小生成树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3366
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=5005,M=2e5+5;
int n,m,fa[N],chosen[N];
bool used[M];
struct
{
	int x,y,w;
}a[M];
int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void boruvka()
{
	bool update=true;
	int res=0,merged=0;
	while(update)
	{
		update=false;
		for(int i=1;i<=n;i++)chosen[i]=0;
		for(int i=1;i<=m;i++)
		{
			if(used[i])continue;
			int x=a[i].x,y=a[i].y,w=a[i].w;
			x=find(x),y=find(y);
			if(x==y)continue;
			if(chosen[x]==0||a[chosen[x]].w>w)chosen[x]=i;
			if(chosen[y]==0||a[chosen[y]].w>w)chosen[y]=i;
		}
		for(int i=1;i<=n;i++)
		{
			if(!chosen[i])continue;
			int x=find(a[chosen[i]].x),y=find(a[chosen[i]].y);
			if(used[chosen[i]]||x==y)continue;
			used[chosen[i]]=true;
			update=true;
			merged++;
			fa[x]=y;
			res+=a[chosen[i]].w;
		}
	}
	if(merged>=n-1)printf("%d\n",res);
	else
		puts("orz");
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
    for(int i=1;i<=n;i++)fa[i]=i;
    boruvka();
    return 0;
}
posted @ 2021-09-17 22:31  zyy2001  阅读(49)  评论(0编辑  收藏  举报