牛客练习赛——牛能和小镇(最小生成树)

牛能和小镇题目

题目
题目
题目链接
刚看到这道题的时候,由于我不懂最小生成树。。。我以为是普通的计算题,第一想法就是通过计算每个小镇最少有一条通往花费最少的小镇。于是我写出了下面的代码。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
ll xz[100010][2];
bool judge[100010]={false};

//a城市到b城市花费 
ll cheek(ll a, ll b)
{
	ll ab;
	ab=abs(pow(xz[a][0], 2)*xz[a][1]-\
	pow(xz[b][0], 2)*xz[b][1]+\
	pow(xz[a][1], 2)*(xz[a][1]-2*xz[a][0])-\
	pow(xz[b][1], 2)*(xz[b][1]-2*xz[b][0]));
	
	return ab;
}

int main()
{
	ll n, temp=1e9+7, sum=0, i, j, a;
	cin>>n;
	for(int i=0; i<n; i++)
	for(int j=0; j<2; j++)
	cin>>xz[i][j];
	
	for(i=0; i<n; i++)
	{	
		if(judge[i]==true)
			continue;
		for(j=0; j<n; j++)
		{
			if(j==i) continue;
			int temp1=min(temp,cheek(i, j));
			if(temp1!=temp)
			{
				a=j;
				temp=temp1;
			}
		}
		judge[a]=true;
		judge[i]=true;
		sum+=temp;
		temp=1e9+7;
	}
	cout<<sum;
	return 0;
}

但是很显然这里的时间复杂度O(n^2)过大,不能够达到AC条件。
在比赛后,我查了下AC代码:

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
ll spent_arr[100010];
int main()
{
	ll x, y, n, sum=0;
	cin>>n;
	for(int i=0; i<n; i++)
	{
		cin>>x>>y;
		spent_arr[i]=x*x*y+y*y*(y-2*x);
	}
	sort(spent_arr, spent_arr+n);
	for(int i=0; i<n-1; i++)
	sum+=spent_arr[i+1]-spent_arr[i];
	cout<<sum;
	
	return 0;
}

根据题目,我们假设要计算A镇(a, b)修路到c镇(c,d)的花费money
money=|a2b-c2d+b2(b-2a)-d2(d-2c)|
变形得到:money=|a2b+b2(b-2a)-c2d-d2(d-2c)|
再变形:money=||a2b+b2(b-2a)|-|c2d-d2(d-2c)||
由这个表达式我们可以知道A镇修路到c镇的花费只与A镇与B镇自身有关,A镇与B镇之间没有联系(两个镇修道路的花费,两镇之间竟然没有联系!)。
设spent=x2y+y2(y-2*x)
我们只用计算出每个小镇的spent值,然后在将每个小镇的spent值由小到大排序得到spent_arr数组。题目要求:每个小镇要互通、花费要最少,spent_arr数组排好序后,依次将相邻的互减取绝对值,最后求和。
sum=spent_arr[1] - spent_arr[0] + spent_arr[2] - spent_arr[1] + … + spent_arr[n] - spent_arr[n-1].

小结:题目是要我们求一颗最小生成树。我们把距离公式交换一下顺序, 排一下序,相邻两点之间连一条边即可得到最小生成树,时间复杂度O(nlogn)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值