1618.剑鱼行动

原题链接

外网进不去

题目大意

给出 N   ( 2 ≤ N ≤ 100 ) N\ (2\le N\le 100) N (2N100) 个点的坐标 x   ( − 10000 ≤ x ≤ 10000 ) , y   ( − 10000 ≤ y ≤ 10000 ) x\ (-10000\le x\le 10000),y\ (-10000\le y\le 10000) x (10000x10000),y (10000y10000) ,对它们建立一个最小生成树,代价就是连接它们的路径的长度,现要求总长度最小。

解题思路

这是一道使用最小生成树的算法,鉴于好理解,我们考虑使用 克鲁斯卡尔(kruskal) 算法(详细讲解见1612.最优布线问题(最小生成树))。
于是,重点就在于如何对每一个点进行编号。不管如何,反正我最先想到的就是使用两个坐标相乘来编码,确实可以通过这一题。但是,我不理解自己是怎么过的! 于是,我决定使用一些科学的方法,直接使用map,将下标使用坐标来表示,但是:
在这里插入图片描述
想了一个上午,终于发现 map 的下标需要比较才能找到正确的位置。又花了30分钟,弄懂了 o p e r a t o r operator operator 的符号重载,对 > , < , ≠ >,<,\ne >,<,= 进行了重载,终于科学地通过了这一题。

代码实现

#include<iostream>
#include<fstream>
#include<set>
#include<map>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
struct node{
	double num,x_1,y_1,x_2,y_2;//记录边
};
struct node2{
	double x,y;//坐标
};
vector<node> Map;
vector<node2> Map2;
vector<node2> Map4;
map<node2,node2> f;//结构体实现的并查集
int n,tot;
double x,y;
bool operator!=(node2 q,node2 h)//重载!=,双关键字
{
	return q.x!=h.x||q.y!=h.y;
}
bool operator>(node2 q,node2 h)//重载>,双关键字
{
	if(q.x!=h.x)
		return q.x>h.x;//第一关键字
	else
		return q.y>h.y;//第二关键字
}
bool operator<(node2 q,node2 h)//重载<,双关键字
{
	if(q.x!=h.x)
		return q.x<h.x;//第一关键字
	else
		return q.y<h.y;//第二关键字
}
bool cmp(node q,node h)//结构体排序
{
	return q.num<h.num;
}
node2 fin(node2 n)//找祖先
{
	if(n!=f[n])
		f[n]=fin(f[n]);
	return f[n];
}
void un(node2 x,node2 y)//合并
{
	f[fin(x)]=fin(y);
}
double dis(double x_1,double y_1,double x_2,double y_2)//计算直线距离
{
	return sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2));//勾股定理
} 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x>>y;
		for(int j=0;j<Map2.size();j++){//遍历已经出现的所有边
			double d=dis(x,y,Map2[j].x,Map2[j].y);
			Map.push_back((node){d,x,y,Map2[j].x,Map2[j].y});//建边
			Map4.push_back((node2){x,y});//记录所有边,初始化时使用
		}
		Map2.push_back((node2){x,y});
	}
	sort(Map.begin(),Map.end(),cmp);//排序
	int k=0;
	double ans=0;
	for(int i=0;i<Map4.size();i++)
		f[Map4[i]]=Map4[i];
	for(int i=0;i<Map.size();i++){//克鲁斯卡尔(kruskal)算法
		node2 q=fin((node2){Map[i].x_1,Map[i].y_1}),h=fin((node2){Map[i].x_2,Map[i].y_2});
		if(q!=h){//未在同一个集合中
			k++;
			ans+=Map[i].num;
			un((node2){Map[i].x_1,Map[i].y_1},(node2){Map[i].x_2,Map[i].y_2});//加入集合
		}
		if(k==n-1){//已经形成了一棵树
			printf("%.2f",ans);
			return 0;
		}
	}
	return 0;
} 

样例1

输入

10
-1817.6 1683.9
-3743.6 2662.6
1988.9 4075.3
-2013.8 -1967.1
-2472.1 4194.8
-3087.5 4224.8
-3424.7 3144.6
585.0 885.4
2504.8 -97.0
2894.5 305.8

输出

16876.35

样例2

输入

100
4006.1 1126.3
-3185.1 -4409.9
1301.0 -4454.9
-559.6 -3365.8
577.0 4742.6
-232.0 -2458.9
305.4 -2091.2
-3392.4 2151.1
-402.7 -2248.0
-1751.9 -679.7
-4178.8 -499.4
1876.8 4892.3
2377.9 -1021.4
4728.2 -3010.3
467.6 655.4
320.0 3481.7
1284.9 -4195.2
-2774.9 582.5
-2729.1 -4068.8
3089.4 3732.5
-106.1 1853.2
-3419.9 -908.2
-3794.0 -4884.1
4318.5 2644.9
3522.8 -410.8
-3603.9 3973.1
3643.4 -2943.8
-842.2 1780.6
1641.6 38.0
4464.1 -2661.0
-177.0 -2584.1
832.1 3374.7
622.6 4118.1
-4223.0 666.3
2547.2 2132.3
1412.9 995.7
-252.3 -3094.5
-3708.6 -2987.0
-4840.2 -1481.5
586.3 1958.6
-3914.2 4525.8
-3756.2 2196.5
1706.7 -4056.7
940.6 -2114.9
-2194.8 2994.9
4114.0 544.6
-4636.6 3641.9
-3581.9 -558.5
-3237.5 -3968.8
-3449.8 1968.0
-4283.5 279.0
-4624.6 2088.6
3680.6 2611.2
1650.6 3750.8
-1061.3 -1587.3
-4613.7 -4728.2
-4310.2 3850.5
-3496.3 1458.4
3802.7 -4547.2
-1638.3 -3659.5
966.5 -159.3
-600.2 2105.5
2486.0 -185.0
279.4 -1668.5
193.8 -2951.1
-4699.9 -1508.2
1732.6 -3658.1
-266.9 3929.6
835.6 -1360.9
3704.1 1863.6
-42.7 -3387.3
-44.9 -220.6
-685.7 1069.8
-2296.7 -4073.4
1288.3 -902.1
2084.5 3577.7
-3594.1 -537.2
-1193.9 3218.4
4818.2 1264.6
-3729.4 -1407.7
-13.3 -2238.7
-3378.3 -2051.6
773.7 -1850.3
-2597.5 -4784.1
4749.6 123.1
-2614.5 1611.2
-1322.7 -1302.1
2774.0 640.4
1748.6 1392.3
-2019.1 3879.7
3885.2 -1352.4
4245.2 -3050.4
-2551.5 3045.9
-3965.5 -1378.6
534.7 -867.6
-3944.2 -2891.0
-761.2 4224.3
1926.2 2839.6
-4574.7 4605.3
-2971.5 -1727.5

输出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值