1.上午三个半小时
学习了最小生成树的prim算法和kruskal算法的基本知识,但因为之前学过并查集,所以对kruskal算法比较熟悉,今天的题目都是用的kruskal算法,后面会尝试用prim算法。
完成了一个题目。
P3366 【模板】最小生成树
P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
首先把每个点的父母结点初始化为结点本身,然后每次加入长度最小的边,判断该边的两个顶点是否在一个集合内,在的话就不要改变什么,否则就把两个点所在的集合并拢,把边的总数、边的长度的总和更新,最后看单独的点,也就是父母节点没有更新的点的数量是否大于一,因为只有根结点的父母结点不变。
代码实现
#include<bits/stdc++.h>
using namespace std;
struct node
{
int x,y,z;
} edge[200001];
bool cmp(node a,node b)
{
return a.z < b.z;
}
int fa[200001];
int n,m;
int u,v,w;
int sum;
int get(int x)
{
return x == fa[x] ? x : fa[x] = get(fa[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i ++)
{
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
}
for(int i = 0; i <= n; i ++)
{
fa[i] = i;
}
sort(edge + 1,edge + 1 + m,cmp);
// 每次加入一条最短的边
for(int i = 1; i <= m; i ++)
{
int x = get(edge[i].x);
int y = get(edge[i].y);
if(x == y) continue;
fa[y] = x;
sum += edge[i].z;
}
int ans = 0;
for(int i = 1; i <= n; i ++)
{
if(i == fa[i]) ans ++;
}
if(ans > 1) puts("orz");
else printf("%d",sum);
return 0;
}
2.下午四个小时
完成了两个题目。
P2121 拆地毯
P2121 拆地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
思路和模板相似,套用模板,这题比模板多了地毯条数这个要求。
代码实现
#include<bits/stdc++.h>
using namespace std;
int n,m,k,sum,ans;
int parent[100001];
struct node
{
int u;
int v;
int w;
} edge[100001];
bool cmp(node x,node y)
{
return x.w>y.w;
}
int Find(int x)
{
return x == parent[x] ? x : parent[x] = Find(parent[x]);
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=m;i++)
scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
for(int i=1;i<=n;i++)
parent[i]=i;
sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=m;i++)
{
int u = Find(edge[i].u);
int v = Find(edge[i].v);
if(u == v) continue;
parent[v] = u;
ans++;
if(ans>k)
break;
sum += edge[i].w;
}
printf("%d",sum);
return 0;
}
P1195 口袋的天空
P1195 口袋的天空 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
继续套用模板,但这个题是要合成规定数量的棉花糖,也就是生成规定数量的树,所以我们如果想要连出k棵树,就需要连n-k条边,如果最终的边数少于n-k,就不可能合成k朵棉花糖,当边数等于n-k时,最小的代价就产生了。
代码实现
#include<bits/stdc++.h>
using namespace std;
int n,m,k,sum,ans;
int parent[100001];
struct node
{
int u;
int v;
int w;
} edge[100001];
bool cmp(node x,node y)
{
return x.w<y.w;
}
int Find(int x)
{
return x == parent[x] ? x : parent[x] = Find(parent[x]);
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=m;i++)
scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
for(int i=1;i<=n;i++)
parent[i]=i;
sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=m;i++)
{
int u = Find(edge[i].u);
int v = Find(edge[i].v);
if(u == v)
continue;
parent[v] = u;
ans++;
sum += edge[i].w;
if(ans>=n-k)
{
printf("%d",sum);
break;
}
}
if(ans<n-k)
printf("No Answer");
return 0;
}
3.晚上一个小时
一个题目没有完全写出来,还要一些错误。先挖个坑,等会再来填。
P1991 无线通讯网
P1991 无线通讯网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路
因为装了卫星电话的哨所之间无论多远都能连通,所以我们就需要把卫星电话装给距离最远的几个哨所,然后去剩下的哨所之间最大的距离定为无线电收发器的最小距离。先算一下每个点之间的距离,然后将两个点和它们之间的距离用结构体数组存起来,同时算一下边的总数,然后每次选取最短的边加入,更新树的结点,找到结点之间最长的距离,最后输出即可。
错误
距离公式不要写错。
计算点之间的距离的时侯,是用sum,而不是i。
代码实现
#include<bits/stdc++.h>
#include<math.h>
using namespace std;
int s,p,sum;
double d=0;
int parent[501];
struct node
{
int u;
int v;
double w;
} edge[250001];
struct pointer
{
double x;
double y;
} point[501];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int Find(int o)
{
return o == parent[o] ? o : parent[o] = Find(parent[o]);
}
int main()
{
scanf("%d %d",&s,&p);
for(int i=1; i<=p; i++)
scanf("%lf %lf",&point[i].x,&point[i].y);
for(int i=1; i<=p; i++)
for(int j=1; j<i; j++)
{
edge[++sum].u=i;
edge[sum].v=j;
edge[sum].w=sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y));
}
for(int i=1; i<=p; i++)
parent[i]=i;
sort(edge+ 1,edge + 1 + sum,cmp);
int ss=0;
for(int i = 1; i <=sum; i ++)
{
int u = Find(edge[i].u);
int v = Find(edge[i].v);
if(u == v)
continue;
parent[v] =u;
if(d<edge[i].w)
d=edge[i].w;
ss++;
if(ss>=p-s)
break;
}
printf("%.2lf",d);
return 0;
}