【洛谷】最小生成树题目笔记


题目链接

P1536 村村通
P1547 Out of Hay
P2820 局域网
P1546 最短网络 Agri-Net
P2330 繁忙的都市


一、村村通

裸的最小生成树……
一道水的不能再水的题……
先贴上代码

#include<bits/stdc++.h>
using namespace std;
int n,m,f[2000020],ans=0;
struct pvp{int t1,t2;}e[2000020];
int getf(int k){if(f[k]==k){return k;}else{return getf(f[k]);}}
void merge(int x,int y){f[getf(x)]=getf(y);}
bool cmp(pvp a,pvp b){return a.t1<b.t1;}
int main()
{
    while(scanf("%d %d",&n,&m)){                                            //一直输入,直到判断n=0时退出循环
        if(n==0){break;}                     
        for(int i=1;i<=n;i++) f[i]=i;                                       //假定每个城镇原来只跟自己有关系,所以全部城镇的身上的数值都不一样
        for(int i=1;i<=m;i++){cin>>e[i].t1>>e[i].t2;merge(e[i].t1,e[i].t2);}//输入哪两个城镇之间有路,然后就把两个城镇之间搞出关系来……
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=n;i++){if(getf(i)==i) ans++;}
        //并查集的基本模型for,因为现在每个城镇的数值和自己的编号已经发生改变,所以,判断有多少个没有变化的,就有多少种关系。当然,最后ans的值要清空。
        cout<<ans-1<<endl;ans=0;}
    return 0;
}

太乱了?看不懂?我也是这样。为了压行……等会就看懂了
首先,最小生成树有两种经典的求法:Kruskal算法,Prim算法
这里用的是Kruskal算法,Kruskal算法用来求稀疏图,Prim算法用来求稠密图
Kruskal算法核心为并查集

  • struct结构体用来存边,t1-t2表示t1与t2相连,如果有权值可加上t3
  • getf函数(又称为find函数)用来把两者变为有关系,如果两者已经有关系或者相同,就返回出关系,如果还没有关系,就一直一直递归下去………
  • merge函数,有没有都无所谓,看起来好看点罢了,其实可以直接用f[getf(e[i].x)]=getf(e[i].y)代替merge(e[i].t1,e[i].t2);},只不过为了好看点。。。
    merge函数目的是合并两者,也就是强迫两者有关系。。。
  • cmp结构体排序函数,其实有没有无所谓(在这道题里),就是让sort排序按照自己想要排序的方法去排序

Out of Hay

也是一道裸题,只不过要求的东西不一样

#include<iostream>
#include<algorithm>
using namespace std;
struct edge{int t1,t2,t3;}e[200001];
int n,m,maxn=0,f[200001],et1,et2,num=0;
bool cmp(edge a,edge b){return a.t3<b.t3;}
int getf(int k)
{
	if(f[k]==k) return k;
	else return getf(f[k]);
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++) cin>>e[i].t1>>e[i].t2>>e[i].t3;
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		if(getf(e[i].t1)==getf(e[i].t2)) continue;                //判断,如果两者已经有了关系,那么不再用两者相连的边,直接continue
		maxn=max(maxn,e[i].t3);                                   //不然,如果这条边比原来的maxn还要大,就记录
		f[getf(e[i].t2)]=getf(e[i].t1);                           //和merge函数一个道理,因为选用了这条边,所以强制他们有关系
		if(++num==n-1) break;                                     //如果已经选用了n-1条边,那么符合最小生成树的性质,就不在继续往下选边   
	}
	cout<<maxn;
	return 0;
}

局域网

#include<iostream>
#include<algorithm>
using namespace std;
struct edge{int t1,t2,t3;}e[200001];
bool cmp(edge a,edge b){return a.t3<b.t3;}
int n,f[200001],m,ans=0,sum=0,et1,et2,k=0;
int getf(int k){if(f[k]==k){return k;}else{return getf(f[k]);}}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++){cin>>e[i].t1>>e[i].t2>>e[i].t3;k+=e[i].t3;}   //k累加的目的是求出所有等到后面求最终答案时更加方便
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		et1=getf(e[i].t1);
		et2=getf(e[i].t2);
		if(et1==et2) continue;
		sum+=e[i].t3;                                                  //如果符合,那么sum累加,其实还有种更简便的做法,就是如果不符合就sum累加,这样不用用到k,也能算出最终答案
		f[et2]=et1;
		if(++ans==n-1) break;
	}
	cout<<k-sum;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值