Highways POJ - 1751 最小生成树
这道题的逻辑比较简单,明白了思路之后,结合并查集模板,我花了两个小时不到的时间写程序,却又花了两个小时去调bug!真是个悲伤的故事啊。
算法是这样的:计算每两个顶点(我设置了包括自环,其实包不包括没太大区别)之间的连线长度,以及记录端点,设为边,设计edge这一结构体存。一共有n*(n+1)/2条边。然后输入已修好的铁路图,用并查集将每两个点相连接。然后,将所有边按照长度升序排序,从第一个边开始,遍历每一条边,如果它的两端点没有在同一个集合内,将它们加入同一个集合(这是并查集的操作),然后输出这两个端点,遍历下一条边。如果在同一个集合内,就跳过。直到所有边都处理完。
bug出在以下几个方面:
1.初始化并查集失败,没有把并查集值初始化为-1。因为犯了一个老错误,在输入一个变量前使用了它。这次因为被我封装成了函数,所以错误很隐蔽,用cout调了好久才发现。
2.两个打错了的变量名。一个是在二重循环中,应该为j++,我打成了i++,这个我调了半个多小时吧;另一个就爽了,是在计算笛卡尔坐标系上的点间距时,我把第二个的结构体下标标成了第一个的,调了我一个半小时还多。
3.由于我标记的城镇是从0n-1的,但是题目中输入和输出都是从1n的,所以我得改我的输入或者输出。由于改输入牵扯到很多下标的变化,出现问题的几率大,所以我设计成了将输出+1,就是对应城市。
这道题带我学了学并查集和kruskal算法求最小生成树,还好吧,除了bug太恶心。
//#include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#define N 1005
using namespace std;
vector<pair<int,int> > pii; //存储每个点的x,y坐标,点集
struct edge{int v1,v2;double cost;}; //定义边的数据结构
vector<edge> ve; //边集
int parent[N],n,m; //parent是并查集的存储数组,某个点i的父节点为parent[i]
double len(int a,int b); //计算两点间距
bool cmp(edge a,edge b) {return a.cost<b.cost;} //对边排序的比较函数
void UFset(); //并查集的初始化,用数组存并查集,将每个点的父节点都设为-1.根节点的父节点值是个负数,绝对值表示当前集合的元素个数
void Union(int a,int b); //将两个点加入同一个集合
int Find(int a); //寻找某个点所在的集合(以这一集合的根节点表示),上三个并查集函数是并查集的核心
bool same(int a,int b); //判断两点是否在同一个集合内
int main() {
pair<int,int> tpii;
edge te;
cin>>n;
for (int i=0;i<n;i++) {
cin>>tpii.first>>tpii.second;
pii.push_back(tpii);
//cout<<"OK"<<endl;
}
UFset(); //因为之前n还未被初始化,一直为0,而该函数初始化数组需要n,所以这句话在n被定义之前无效
//cout<<"pii:\t"<<pii.size()<<endl;
for (int i=0;i<(int)pii.size();i++) {
for (int j=i;j<(int)pii.size();j++) {
te.v1=i,te.v2=j; //无向图,又忘了,边得输入两次,但是在这题不是邻接矩阵,而相当于邻接表,所以只输入一条联通边即可。
if (i==j) te.cost=0;
else te.cost=len(i,j);
//cout<<te.cost<<endl;
ve.push_back(te);
}
}
cin>>m;
for (int i=0;i<m;i++) {
int t1,t2;
cin>>t1>>t2;
Union(t1-1,t2-1); //将已经相连的点连接,因为我是从0开始的,但是输入从1开始,所以此处-1
}
// for (int i=0;i<n;i++)
// cout<<parent[i]<<' ';
// cout<<endl;
sort(ve.begin(),ve.end(),cmp); //对边排序,kruskal算法的要求
//cout<<"TABLE\n";
// for (int i=0;i<(int)ve.size();i++)
// cout<<ve[i].v1<<' '<<ve[i].v2<<' '<<ve[i].cost<<endl;
//cout<<"ve:\t"<<ve.size()<<endl;
for (int i=0;i<(int)ve.size();i++) {
if (!same(ve[i].v1,ve[i].v2)) { //如果没在同一个并查集中,说明这两点未相连,就连接他们两个
Union(ve[i].v1,ve[i].v2);
cout<<ve[i].v1+1<<' '<<ve[i].v2+1<<endl;
}
}
return 0;
}
double len(int a,int b) {
double dx=pii[a].first-pii[b].first;
double dy=pii[a].second-pii[b].second;
//cout<<"dxdy:\t"<<dx<<' '<<dy<<endl;
//cout<<ans<<endl;
return sqrt(1.0*dx*dx+1.0*dy*dy);
}
void UFset() {fill(parent,parent+n,-1);}
void Union(int a,int b) {
int r1=Find(a),r2=Find(b);
if (r1==r2) return;
//cout<<"Find is OK"<<endl;
int R=parent[r1]+parent[r2];
if (parent[r1]<parent[r2]) {
parent[r2]=r1;
parent[r1]=R;
}
else {
parent[r1]=r2;
parent[r2]=R;
}
}
int Find(int a) {
int res=a;
for (;parent[res]>=0;res=parent[res]);
//cout<<"now res=\t"<<res<<endl;
while (res!=a) {
int tp=parent[a];
parent[a]=res;
a=tp;
//cout<<"a=\t"<<a<<endl;
}
return res;
}
bool same(int a,int b) {
if (Find(a)==Find(b)) return true;
return false;
}