2 条题解
-
0

// Kruskal算法 O(mlogm) #include<bits/stdc++.h> #define int long long #define pii pair<int,int> using namespace std; const int N=1e5+5,M=3.2e6+5; int n,m,ans,tot,fa[N]; struct A{int x,y;} a[N]; //坐标 struct E{int x,y,d;} e[M]; //边集 int find(int u){ //并查集的找根 return fa[u]==u?u:fa[u]=find(fa[u]); } void kruskal(){ for(int i=1;i<=n;i++) fa[i]=i; sort(e+1,e+1+m,[&](E a,E b){return a.d<b.d;}); for(int i=1;i<=m;i++){ int x=find(e[i].x),y=find(e[i].y); if(x!=y){ fa[x]=y; ans+=e[i].d; if(++tot==n-1) break; } } printf("%lld\n",ans); } signed main(){ scanf("%lld",&n); for(int i=1,x,y;i<=n;i++){ scanf("%lld%lld",&x,&y); a[i]={x,y}; } sort(a+1,a+n+1,[&](A a,A b){ if(a.x!=b.x) return a.x<b.x; return a.y<b.y; }); for(int i=1;i<=n-1;i++)for(int j=i+1; j<=min(i+32,n); j++){ e[++m]={i,j,(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)}; } kruskal(); } -
0
题目传送门:P8191 [USACO22FEB] Moo Network G
题目大意:
一共有 头牛,给你它的位置, 和 。两头牛的交流成本是欧几里得距离的平方。求,最低的成本。
分析:
首先,看到“最低成本”这几个字,立马反应,这是一道题的考点是最小生成树。
这里应该有两个排序,一个是把坐标从小到大排序,另一个是把距离从小到大排序。
这里还需要存个图, 代表的是两者间的距离。
思路:
用 Kruskal,因为这个好写一点。
然后,它的权值是距离,距离计算用一个 函数计算就好啦。
用一个 struct 类型的数组
g[]存图,g[].x和g[].y存位置,g[].v存距离。用一个 存边。到时候排序的时候不是g+n+1,而是g+m+1。向左找 30 个点连边不会错。因为 ,所以最小生成树的边,都在这里面建的边当中。
注意:
题目说是欧几里德距离的平方,前面就不需要写
sqrt了。也不用double了。然后要开
long long,不然只有 分。最优的话是连 条边。
最后代码
#include<bits/stdc++.h> using namespace std; const int N=1e6+5,M=4e6+5; #define int long long int n,m; int fa[N]; int cnt,ans; struct node { int x,y,v; }g[M];//它存的是图,所以范围要大一些 struct Node { int x,y; }a[N]; int find(int x)//这里是找自己的根节点 { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void un(int x,int y)//这里是合并 { x=find(x),y=find(y); if(x!=y) fa[x]=y; return ; } bool cmp(node q,node p)//将距离从小到大排序 { return q.v<p.v; } bool Cmp(Node q,Node p)//将位置从小到大排序 { if(q.x!=p.x) return q.x<p.x; return q.y<p.y; } void k()//kruskal啦,只是太长不想打那么多而已 { for(int i=1;i<=n;i++)//初始化,一开始每个人的根节点都是自己 fa[i]=i; sort(g+1,g+m+1,cmp);//距离排序 cnt=ans=0;//保险起见,初始值为0 for(int i=1;i<=m;i++)//注意!这里不是n { if(find(g[i].x)!=find(g[i].y))//看它们有没有合并过 { un(g[i].x,g[i].y);//合并 ans+=g[i].v;//加上距离,也就是成本 cnt++;//代表连的边+1 } if(cnt==n-1)//一般是只有n-1条边( 最优),不过不写也没关系 return ; } return ; } double dist(int x1,int y1,int x2,int y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);//这里是欧几里得算法距离的平方,也就是成本 } signed main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;//输入位置 sort(a+1,a+n+1,Cmp);//将位置从小到大排序 for(int i=1;i<=n-1;i++) { for(int j=i;j<=i+30;j++)//这里是为了保险,加了30意思是连它前30个点 { m++;//这里4行是存图 g[m].x=i; g[m].y=j; g[m].v=dist(a[i].x,a[i].y,a[j].x,a[j].y); } } k();//调用函数 cout<<ans;//也可以在k函数里面输出 return 0; }
- 1
信息
- ID
- 7641
- 时间
- 4000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 35
- 已通过
- 4
- 上传者