每日总结2023.2.2(Building Roads S,租用游艇)

 P2872 [USACO07DEC]Building Roads Shttps://www.luogu.com.cn/problem/P2872

题目描述

FarmerJohn最近得到了一些新的农场,他想新修一些道路使得他的所有农场可以经过原有的或是新修的道路互达(也就是说,从任一个农场都可以经过一些首尾相连道路到达剩下的所有农场)。有些农场之间原本就有道路相连。 所有N(1<=N<=1,000)个农场(用1..N顺次编号)在地图上都表示为坐标为(Xi​,Yi​)的点(0 <= X_i <= 1,000,000;0 <= Y_i <= 1,000,000),两个农场间道路的长度自然就是代表它们的点之间的距离。现在FarmerJohn也告诉了你农场间原有的M(1<=M<=1,000)条路分别连接了哪两个农场,他希望你计算一下,为了使得所有农场连通,他所需建造道路的最小总长是多少。

输入格式

第一行两个整数 n,m 代表点数与边数。
接下来 n 行每行两个整数 xi​,yi​ 代表第 i 个点的坐标。
接下来 m 行每行两个整数 ui​,vi​ 代表第 i 条边连接第 ui​ 个点和第 vi​ 个点。

输出格式

一行一个实数代表添加的边的最小长度,要求保留两位小数,为了避免误差, 请用 64 位实型变量进行计算。

输入输出样例

输入 #1复制

4 1
1 1
3 1
2 3
4 3
1 4

输出 #1复制

4.00

说明/提示

数据规模与约定

对于 100%100% 的整数,1≤n,m≤1000,1≤xi​,yi​≤10^6,1≤ui​,vi​≤n。

思路

  1. 依据原有的边,将能连起来的点先连起来。 
  2. 然后建立未能连接起来的点之间的边。
  3. 对建立起来的边排序
  4. Kruskal

代码实现 

#include<bits/stdc++.h>
using namespace std;

struct edge{
	int u;
	int v;
	double w;
}e[1000000+5];

struct point{
	int x;
	int y;
}p[1000+5];

double dis(point a,point b)//计算俩点间距离
{
	return sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y));
}

void init(int *parent,int n)
{
	for(int i=0;i<=n;i++)
	{
		parent[i]=i;
	}
}

int find(int *parent,int x)
{
	while(parent[x]!=x)
	{
		parent[x]=parent[parent[x]];
		x=parent[x];
	}
	return x;
}

int link(int *parent,int a,int b)
{
	int a_root=find(parent,a);
	int b_root=find(parent,b);
	if(a_root==b_root)
	{
		return 0;
	}
	else
	{
		parent[a_root]=b_root;
		return 1;
	}
}

void quicksort(int l,int r)
{
	if(l>=r) return;
	int i=l;
	int j=r;
	while(i!=j)
	{
		while(e[j].w>=e[l].w&&j>i)
		{
			j--;
		}
		while(e[i].w<=e[l].w&&i<j)
		{
			i++;
		}
		edge temp=e[i];
		e[i]=e[j];
		e[j]=temp;	
	}
	edge temp=e[l];
	e[l]=e[i];
	e[i]=temp;
	quicksort(l,i-1);
	quicksort(i+1,r);
}

int main()
{
	int parent[1000+5];
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&p[i].x,&p[i].y);
	}
	init(parent,n);
	int u,v;
	for(int i=1;i<=m;i++)//已经存在的边,就把俩点连接起来
	{
		scanf("%d%d",&u,&v);
		link(parent,u,v);
	}
	int cnt=0;
	for(int i=1;i<=n;i++)//存边
	{
		for(int j=i+1;j<=n;j++)//不需要和之前的点重复建边
		{
			if(find(parent,i)!=find(parent,j))
			{
				e[++cnt].u=i;
				e[cnt].v=j;
				e[cnt].w=dis(p[i],p[j]);
			}
		}
	}
	quicksort(1,cnt);
	double ans=0.0;
	int flag;
	for(int i=1;i<=cnt;i++)
	{
		flag=link(parent,e[i].u,e[i].v);
		if(flag)//俩点成功连接
		{
			ans+=e[i].w;
		}
	}
	printf("%.2lf",ans);
	return 0;
}

注意:edge数组开大一点,10^6最稳妥


P1359 租用游艇https://www.luogu.com.cn/problem/P1359

题目描述

长江游艇俱乐部在长江上设置了 n 个游艇出租站 1,2,⋯,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i 到游艇出租站 j 之间的租金为 r(i,j)     (1≤i<j≤n)。试设计一个算法,计算出从游艇出租站 1到游艇出租站 n 所需的最少租金。

输入格式

第一行中有一个正整数 n,表示有 n 个游艇出租站。接下来的 n−1 行是一个半矩阵 (i,j)(1≤i<j≤n)。

输出格式

输出计算出的从游艇出租站 1 到游艇出租站 n 所需的最少租金。

输入输出样例

输入 #1复制

3
5 15
7

输出 #1复制

12

说明/提示

n≤200,保证计算过程中任何时刻数值都不超过10^6。

思路:以1为源点的Dijkstra。需要注意的是半矩阵,第i行第j个输入表示,i到j+i的费用。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>

int main()
{
	int dis[200+5]={0};
	int book[200+5]={0};
	int n;
	scanf("%d",&n);
	int r[205][205]={0};
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&dis[i]);
		r[1][i]=dis[i];
	}
	for(int i=2;i<n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			scanf("%d",&r[i][j]);
		}
	}
	int near;
	for(int i=1;i<n;i++)
	{
		int min=1000000;
		for(int j=2;j<=n;j++)
		{
			if(book[j]==0&&dis[j]<min)
			{
				min=dis[j];
				near=j;
			}
		}
		if(near==n)
		{
			printf("%d",dis[n]);
			return 0;
		}
		book[near]=1;
		for(int j=near+1;j<=n;j++)//不用回头,直接near+1
		{
			if(book[j]==0&&dis[j]>dis[near]+r[near][j])
			{
				dis[j]=dis[near]+r[near][j];
			}
		}
	
	}
	printf("%d",dis[n]);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值