每日一题:通电(最小生成树算法)

2020.10.16第二次每日一题

题目:通电

在这里插入图片描述

解决的方法:

要让所有的村庄连在一起,并且花费最小,标准的最小生成树问题
什么是最小生成树呢?网上搜都能搜到。常用的最小生成树算法一般是两个:
1.Prim算法:不断加点的方式;2.Kruskal算法:不断加边的方式,这里先给出Kruskal的算法。

Kruskal算法

首先就是存点和边,然后我们使用到的数组尽量在主函数外面开,在里面开直接MLE了

const int MAX = 1005;
int a[MAX], parent[MAX];
int n;

struct edge
{
    int start;
    int end;
    int cost;

} edges[MAX * (MAX - 1) / 2];

struct node
{
    int x;
    int y;
    int h;

} Node[MAX];

然后定义的是边权值是如何计算的,还有并查集的三个函数

//计算边的权值
double cost(node a,node b)
{
	double xx=a.x-b.x;
	double yy=a.y-a.y;
	double hh=a.h-b.h;
    return sqrt(xx*xx+ yy*yy) + hh*hh;
}
int get_root(int a)
{
    if (parent[a] != a)
    {
        parent[a] = get_root(parent[a]);
    }
    
    return parent[a];
}

void Merge(int a, int b)
{
    parent[get_root(a)] = get_root(b);
}

bool Query(int a, int b)
{
    return get_root(a) == get_root(b);
}

接下来是主函数

int main()
{
    int n, num = 0;
    cin >> n;
    initial(n);
    for (int i = 0; i < n; i++)
    {
        cin >> Node[i].x >> Node[i].y >> Node[i].h;
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n && j != i; j++)
        {
            edges[num].start = i;
            edges[num].end = j;
            edges[num].cost = cost(Node[i], Node[j]);
            num++;
        }
    }

    sort(edges, edges + num);

    double sum = 0.0;
    int count=0;
    for (int k = 0; k < num; k++)
    {
        /*if (!Query(edges[k].start, edges[k].end))
        {
            Merge(edges[k].start, edges[k].end);
            sum+=edges[k].cost;
            count++;
        }
		*/
		int start = edges[k].start;
		int end = edges[k].end;
		if(get_root(start) != get_root(end))
		{
			count++;
			sum+=edges[k].cost;
			if(count == n-1)
			{
				break;
			}
		}
    }

    cout<<setiosflags(ios::fixed)<<setprecision(2)<<sum<<endl;//C++中的保留小数的输出函数
    return 0;
}

修改

感觉很好啊,功能都实现了,应该可以一次性通过啊,结果经历了漫长的修改之路
开始报错是,sort函数使用错误,因为我们是对结构体数组进行排序,所以要自己写一个比较的函数,告诉电脑该如何去排序

bool cmp(edge a,edge b)
{
	rerturn a.cost < b.cost;
}

//主函数里也需要修改
    sort(edges, edges + num , cmp);

发现还是不对哇,发现如果用函数去调用会发生爆栈,直接 MLE了,只好去主函数里修改了一下,怎么办呢?我们就不调用那几个函数了,直接用语句去替换

if(get_root(start) != get_root(end))
		{
			count++;
			sum+=edges[k].cost;
			if(count == n-1)
			{
				break;
			}
		}

结果发现还是不太对,搞死搞不出来,结果竟然是14,样例都不对
从头到尾检查一遍,发现原来困扰了1个小时的问题竟然是这里

double cost(node a,node b)
{
	double xx=a.x-b.x;
	double yy=a.y-a.y;//就是这
	double hh=a.h-b.h;
    return sqrt(xx*xx+ yy*yy) + hh*hh;
}

好吧,我尽力了,这就是刷题的快乐,成功的那一刻喜悦之情是溢于言表的、

完整代码

#include <iostream>
#include <cmath>
#include <algorithm>
#include <bits/stdc++.h>
#include <iomanip>
using namespace std;

const int MAX = 1005;
int a[MAX], parent[MAX];
int n;

struct edge
{
    int start;
    int end;
    double cost;

} edges[MAX * (MAX - 1) / 2];

struct node
{
    int x;
    int y;
    int h;

} Node[MAX];

double cost(node a,node b)
{
	double xx=a.x-b.x;
	double yy=a.y-a.y;
	double hh=a.h-b.h;
    return sqrt(xx*xx+ yy*yy) + hh*hh;
}
void initial(int n)
{
    for (int i = 1; i <= n; i++)
    {
        parent[i] = i;
    }
}

int get_root(int a)
{
    if (parent[a] != a)
    {
        parent[a] = get_root(parent[a]);
    }
    
    return parent[a];
}

void Merge(int a, int b)
{
    parent[get_root(a)] = get_root(b);
}

bool Query(int a, int b)
{
    return get_root(a) == get_root(b);
}

bool cmp(edge a,edge b)
{
	return a.cost<b.cost;
}

int main()
{
    int n, num = 0;
    cin >> n;
    initial(n);
    for (int i = 0; i < n; i++)
    {
        cin >> Node[i].x >> Node[i].y >> Node[i].h;
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n && j != i; j++)
        {
            edges[num].start = i;
            edges[num].end = j;
            edges[num].cost = cost(Node[i], Node[j]);
            num++;
        }
    }

    sort(edges, edges + num ,cmp);

    double sum = 0.0;
    int count=0;
    for (int k = 0; k < num; k++)
    {
        /*if (!Query(edges[k].start, edges[k].end))
        {
            Merge(edges[k].start, edges[k].end);
            sum+=edges[k].cost;
            count++;
        }
		*/
		int start = edges[k].start;
		int end = edges[k].end;
		if(get_root(start) != get_root(end))
		{
			count++;
			sum+=edges[k].cost;
			if(count == n-1)
			{
				break;
			}
		}
    }

    cout<<setiosflags(ios::fixed)<<setprecision(2)<<sum<<endl;
    return 0;
}

昨天去紫金山实习去了,今天继续补题了…

近期预告:
线段树、树状数组、状压DP、Trie树、图论

如果大家有什么建议或者要求请后台留言
联系方式:shirandexiaowo@foxmail.com

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Shirandexiaowo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值