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;
}

posted @ 2018-10-16 13:59  CF过2100就买ARCTERYX  阅读(190)  评论(0编辑  收藏  举报