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。
思路:
- 依据原有的边,将能连起来的点先连起来。
- 然后建立未能连接起来的点之间的边。
- 对建立起来的边排序
- 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;
}