放假回来的第一天,也是直接开刷本周的任务,感觉很顺利(可能是沉淀了太久),下面发一下今天刷题的题解
题目链接:P1991 无线通讯网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解题思路:将每个点之间的距离算出来(勾股定理)用结构体存储点与点之间的距离关系,依据点与点之间的距离将其排成升序(距离远就用卫星电话),遍历结构体,判断该点是否被连通过,确定循环边界,输出答案
代码实现:
#include<stdio.h>
#include<math.h>
#define N 510
int f[N];
int a[N],b[N];
int s,p;
int k,sum;
double res;
struct node//定义结构体
{
int x;
int y;
double z;
}arr[1000000];
void init()//初始化函数
{
for(int i=1;i<=p;i++)
f[i]=i;
}
int find(int x)//并查集中的“查”
{
if(x==f[x])
return x;
else
{
f[x]=find(f[x]);
return f[x];
}
}
void qsort(int l,int r)//快排函数
{
double temp=arr[(l+r)/2].z;
int i=l,j=r;
while(1)
{
if(i>=j)
break;
while(arr[j].z>temp)
j--;
while(arr[i].z<temp)
i++;
if(i<=j)
{
struct node temp1=arr[i];
arr[i]=arr[j];
arr[j]=temp1;
i++;
j--;
}
}
if(j>l)
qsort(l,j);
if(i<r)
qsort(i,r);
}
int main()
{
scanf("%d %d",&s,&p);
init();
for(int i=1;i<=p;i++)
{
scanf("%d %d",&a[i],&b[i]);
for(int j=1;j<i;j++)
{
arr[++k].x=i;
arr[k].y=j;
arr[k].z=sqrt((a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]));//勾股定理求两点之间的距离
}
}
// for(int i=1;i<=k;i++)
// printf("%d %d %.2lf\n",arr[i].x,arr[i].y,arr[i].z);
// printf("截止\n");
qsort(1,k);
// for(int i=1;i<=k;i++)
// printf("%d %d %.2lf\n",arr[i].x,arr[i].y,arr[i].z);
for(int i=1;i<=k;i++)
{
int t1=find(arr[i].x);
int t2=find(arr[i].y);
if(t1!=t2)//判断是否连接过
{
sum++;
f[t2]=t1;
res=arr[i].z;
if(sum==p-s)//出循环条件
{
printf("%.2lf",res);
return 0;
}
}
}
}
题目链接:P1396 营救 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解题思路:题目要求经过道路的拥挤度最大值的最小(直接排成升序),遍历结构体,每遍历一次就判断一次是否能到达目的地,能就直接输出当前遍历到的拥挤度,否则继续遍历,直到能到目的地即可
代码实现:
#include<stdio.h>
#define N 20010
int n,m,s,t;
int f[N];
struct node//定义结构体
{
int x;
int y;
int z;
}a[1000000];
int find(int x)//并查集中的“查”
{
if(x==f[x])
return x;
else
{
f[x]=find(f[x]);
return f[x];
}
}
void init()//初始化函数
{
for(int i=1;i<=n;i++)
f[i]=i;
}
int qsort(int l,int r)//快排函数
{
int temp=a[(l+r)/2].z;
int i=l,j=r;
while(1)
{
if(i>=j)
break;
while(a[j].z>temp)
j--;
while(a[i].z<temp)
i++;
if(i<=j)
{
struct node temp1=a[i];
a[i]=a[j];
a[j]=temp1;
i++;
j--;
}
}
if(j>l)
qsort(l,j);
if(i<r)
qsort(i,r);
}
int pd()//判断函数
{
if(find(f[s])==find(f[t]))
return 1;
else
return 0;
}
int main()
{
scanf("%d %d %d %d",&n,&m,&s,&t);
init();//初始化
for(int i=1;i<=m;i++)
scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].z);
qsort(1,m);//排序
// for(int i=1;i<=m;i++)
// printf("%d ",a[i].z);
for(int i=1;i<=m;i++)
{
int t1=find(a[i].x);
int t2=find(a[i].y);
if(t1!=t2)
f[t2]=t1;
if(pd())//满足条件就输出
{
printf("%d",a[i].z);
return 0;
}
}
}
题目链接:P2872 [USACO07DEC] Building Roads S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
解题思路:跟无线通讯网很像,先把每个点之间的距离用勾股定理算出来,再将其排成升序,遍历结构体,这里要注意的是题目给了我们部分边,要把这些边变成0,或者把这两个点合并(两钟都可以,只是出循环的条件不同而已)
代码实现:
第一种:将已知成边的两点合并
#include<stdio.h>
#include<math.h>
#define N 1010
int f[N];
int n,m,k;
int a1[N],a2[N];
double res;
struct node//定义结构体
{
int x;
int y;
double z;//距离要用浮点型
}a[1000000];
void init()//初始化函数
{
for(int i=1;i<=n;i++)
f[i]=i;
}
int find(int x)//并查集
{
if(x==f[x])
return x;
else
{
f[x]=find(f[x]);
return f[x];
}
}
int qsort(int l,int r)//快排函数
{
double temp=a[(l+r)/2].z;
int i=l,j=r;
while(1)
{
if(i>=j)
break;
while(a[j].z>temp)
j--;
while(a[i].z<temp)
i++;
if(i<=j)
{
struct node temp1=a[i];
a[i]=a[j];
a[j]=temp1;
i++;
j--;
}
}
if(j>l)
qsort(l,j);
if(i<r)
qsort(i,r);
}
int pd()//判断函数
{
int sum=0;
for(int i=1;i<=n;i++)
if(f[i]==i)
{
sum++;
if(sum==2)
return 0;
}
return 1;
}
int main()
{
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=n;i++)
{
scanf("%d %d",&a1[i],&a2[i]);
for(int j=1;j<i;j++)
{
a[++k].x=i;
a[k].y=j;
a[k].z=sqrt((double)(a1[i]-a1[j])*(double)(a1[i]-a1[j])+(double)(a2[i]-a2[j])*(double)(a2[i]-a2[j]));//勾股定理求距离,这里在sqrt里面要用到强制转换将其转换成浮点型
}
}
for(int i=1;i<=m;i++)//合并已经成边的两个点(也可以将其边变成0存入结构体中)
{
int u,v;
scanf("%d %d",&u,&v);
int t1=find(u);
int t2=find(v);
f[t2]=t1;
}
qsort(1,k);
// for(int i=1;i<=k;i++)
// printf("%d %d %.2lf\n",a[i].x,a[i].y,a[i].z);
// printf("\n");
for(int i=1;i<=k;i++)//这里的循环条件为k而不是n,找了好久都没找到!!!
{
int t1=find(a[i].x);
int t2=find(a[i].y);
if(t1!=t2)
{
f[t2]=t1;
res+=a[i].z;
if(pd())
{
printf("%.2lf",res);
return 0;
}
}
}
}
第二种:将已经成边的两点的边变成0存入结构体数组中
#include<stdio.h>
#include<math.h>
#define N 1010
int f[N];
int n,m,k;
int a1[N],a2[N];
double res;
int flog;//判断整个图是否联通
struct node//定义结构体
{
int x;
int y;
double z;
}a[1000000];
void init()//初始化函数
{
for(int i=1;i<=n;i++)
f[i]=i;
}
int find(int x)//并查集
{
if(x==f[x])
return x;
else
{
f[x]=find(f[x]);
return f[x];
}
}
int qsort(int l,int r)//快排函数
{
double temp=a[(l+r)/2].z;
int i=l,j=r;
while(1)
{
if(i>=j)
break;
while(a[j].z>temp)
j--;
while(a[i].z<temp)
i++;
if(i<=j)
{
struct node temp1=a[i];
a[i]=a[j];
a[j]=temp1;
i++;
j--;
}
}
if(j>l)
qsort(l,j);
if(i<r)
qsort(i,r);
}
int main()
{
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=n;i++)
{
scanf("%d %d",&a1[i],&a2[i]);
for(int j=1;j<i;j++)
{
a[++k].x=i;
a[k].y=j;
a[k].z=sqrt((double)(a1[i]-a1[j])*(double)(a1[i]-a1[j])+(double)(a2[i]-a2[j])*(double)(a2[i]-a2[j]));//勾股定理求两点间距离
}
}
for(int i=1;i<=m;i++)//将输入的已成边的长度变成0
{
int u,v;
scanf("%d %d",&u,&v);
a[++k].x=u;
a[k].y=v;
a[k].z=0;
}
qsort(1,k);
// for(int i=1;i<=k;i++)
// printf("%d %d %.2lf\n",a[i].x,a[i].y,a[i].z);
// printf("\n");
for(int i=1;i<=k;i++)//循环条件为k而不是n
{
int t1=find(a[i].x);
int t2=find(a[i].y);
if(t1!=t2)
{
f[t2]=t1;//合并两点
res+=a[i].z;
flog++;
if(flog==n-1)//出循环边界
{
printf("%.2lf",res);
return 0;
}
}
}
}
以上两种处理方式都可以ac,多写一种方法也能锻炼我们的能力,对最小生成树的理解也会更加深刻