每日一题——最小生成树(哭死)

这道题考察最小生成树和贪心。

万物皆用kruscal(滑稽)

最小生成树的板子:

void kruscal() {
	for(int i=1; i<=n; i++) f[i]=i;
	sort(Edge+1,Edge+1+m,cmp);
	for(int i=1; i<=m; i++) {
		int u=Edge[i].u,v=Edge[i].v;
		int k=find(u),l=find(v);
		if(k!=l) {
			f[k]=l;
			sum+=w;
			cnt++;
			if(cnt==n-1) return;
		}
	}
}

题目给定N个点需要N-1条隧道使得它们直接或间接的连在一起。

假如我们暴力求解,给它们每个点之间都连一条边,就要连n*(n-1)/2条边,n=1e5时,需要存边数为5*10^9条,肯定会MLE,所以我们需要优化一下。

那么怎么优化呢?删边,删掉没有贡献的边就好了。其实我们不需要每个点都连一条边,只需要相邻的点连一条边就好了。


每一条边是同一维度中的差的绝对值的最小值,那么我们完全不需要建那么多条边,只需要每两个相邻的同一纬度建边,即3*(n-1)条边,总共才300000条边。

比如样例1:它有两个点,我们就需要建3条边,即两个点之间的x轴、y轴、z轴各建一条边。由于题目中给出了绝对值,所以我们最好分别对这些点的各个维度进行的值进行从小到大排序,防止算边的长度时出现负数。

然后我们就需要将这些边存起来。比如第一和第二个点之间有三条边,第二和第三个点之间又有三条边,那么我们就得用到邻接表来存储边,这样才可以保证每一条边都有名字。

最后我们套上kruscal板子就好啦!

完整代码:

//两个点的坐标之间最大的差值是2e9,在int类型里面
#include <bits/stdc++.h>
#define MOD 1000000007
#define INF 0x7f7f7f7f
using namespace std;
typedef long long LL;
int n,idx,minn,f[100005];

struct node {
	int x,y,z,id;
	//node uu(int x,int y,int z,int id):x(x),y(y),z(z),id(id) {}
} point[100005];//存点

struct edge {
	int u,v,w;
	//node uu(int u,int v,int w):u(u),v(v),w(w) {}
} Edge[300005];//存边

//以x坐标为基准升序
bool cmpx(node a,node b) {
	return a.x<b.x;
}

//以y坐标为基准升序
bool cmpy(node a,node b) {
	return a.y<b.y;
}

//以z坐标为基准升序
bool cmpz(node a,node b) {
	return a.z<b.z;
}

//以两个对应维度之间的长度为基准升序
bool cmpw(edge a,edge b) {
	return a.w<b.w;
}

int find(int x) {
	return f[x]==x ? x : f[x]=find(f[x]);
}

//克鲁斯卡尔模板
void kruscal() {
	for(int i=1; i<=n; i++) f[i]=i;//初始化
	int cnt=0;
	sort(Edge+1,Edge+idx+1,cmpw);//对边进行升序
	//for(int i=1; i<=idx; i++)
	//cout<<Edge[i].w<<endl;
	for(int i=1; i<=idx; i++) {
		int u=Edge[i].u, v=Edge[i].v, w=Edge[i].w;
		int k=find(u),l=find(v);
		//cout<<k<<" "<<l<<endl;
		if(k!=l) {
			f[k]=l;
			minn+=w;
			cnt++;
			if(cnt==n-1) return;
		}
	}
}

//邻接表用于存边
void add(int a,int b,int c) {
	Edge[++idx].u=a;
	Edge[idx].v=b;
	Edge[idx].w=c;
}
//快读
inline int read() {
	int date=0,w=1;
	char c;
	c=getchar();
	while(c<'0' || c>'9') {
		if(c=='-') w=-1;
		c=getchar();
	}
	while(c>='0' && c<='9') {
		date=date*10+(c-'0');
		c=getchar();	
	}
	return date*w;
}

int main() {
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>point[i].x>>point[i].y>>point[i].z;
		point[i].id=i;
	}

	sort(point+1,point+1+n,cmpx);
	for(int i=1; i<n; i++)
		add(point[i].id, point[i+1].id, point[i+1].x-point[i].x);

	sort(point+1,point+1+n,cmpy);
	for(int i=1; i<n; i++)
		add(point[i].id, point[i+1].id, point[i+1].y-point[i].y);

	sort(point+1,point+1+n,cmpz);
	for(int i=1; i<n; i++)
		add(point[i].id, point[i+1].id, point[i+1].z-point[i].z);

	kruscal();
	cout<<minn;
	return 0;//完美收尾
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_谦言万语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值