POJ 1984 Navigation Nightmare

本文介绍了一种使用并查集算法来计算农场间相对坐标的高级应用案例。通过在查找和合并过程中更新参照点的方式,实现了对每个农场相对位置的有效追踪。文章详细展示了算法的具体实现过程,包括如何处理道路连接和查询请求。

/*
并查集的高级应用,利用并查集计算农场间的相对坐标

关系在每次查找和合并的时候由于参照点的变化需要更

新相应点的坐标
*/
#include <iostream>
#include <cmath>
#define MAX_F 40005
#define MAX_Q 10005

//存储所有农场的位置
struct farm
{
    //并查集的参照点id以及当前点的排名
    int sid, rank;
    //当前农场的相对位置坐标(相对参照点)
    int x, y;
}farms[MAX_F + 1];

//存储输入的road
struct con
{
    //左右端点以及距离
    int from, to, dist;
    //路的方向
    char dir;
}cons[MAX_F + 1];

//存储查询
struct query
{
    int from, to;
    int index;
}querys[MAX_Q + 1];

//农场的个数,路的个数以及查询的个数
int fn, cn, qn;

//寻找农场fid的参照点
int find(int fid)
{
    //自己就是自己的参照点
    if(farms[fid].sid == fid) return fid;

    //当前的参照点
    int pre = farms[fid].sid;
    //更新参照点以及更新当前农场的相对坐标
    farms[fid].sid = find(farms[fid].sid);
    farms[fid].x += farms[pre].x;
    farms[fid].y += farms[pre].y;
    return farms[fid].sid;
}

//组合农场fid1以及fid2; chx以及chy是fid2相对于fid1的坐标,以东和北为正方向
void join(int fid1, int fid2, int chx, int chy)
{
    //找到两个农场的参照点
    int sid1 = find(fid1), sid2 = find(fid2);
    //如果参照点相同则无需其他工作
    if(sid1 == sid2) return;
    else
    {
        //否则需要合并两个集合并定义新集合的参照点,按秩组合提高效率
        if(farms[sid1].rank < farms[sid2].rank)
        {
            //农场fid1的原参照点sid1需要以农场fid2的参照点sid2为新的参照点并更新相对坐标
            farms[sid1].x = farms[fid2].x - farms[fid1].x - chx;
            farms[sid1].y = farms[fid2].y - farms[fid1].y - chy;
            farms[sid1].sid = sid2;
        }
        else
        {
            //农场fid2的原参照点sid2需要以农场fid1的参照点sid1为新的参照点并更新相对坐标
            farms[sid2].x = farms[fid1].x - farms[fid2].x + chx;
            farms[sid2].y = farms[fid1].y - farms[fid2].y + chy;
            farms[sid2].sid = sid1;
            //增加秩
            if(farms[sid1].rank == farms[sid2].rank)
                farms[sid1].rank++;
        }
    }
}

//处理查询请求
int querySolve(int fid1, int fid2)
{
    int sid1 = find(fid1), sid2 = find(fid2);
    //如果参照点不同则当前无法判断相对坐标关系
    if(sid1 != sid2) return -1;
    //否则计算曼哈顿距离
    else return abs(farms[fid1].x - farms[fid2].x) + abs(farms[fid1].y - farms[fid2].y);
   
}

void init()
{
    for(int i = 0; i < fn; i++)
        farms[i].sid = i;
}
int main()
{
    int i;
    scanf("%d%d", &fn, &cn);
    init();
    //getchar();
    //需要将输入以及查询存储起来,然后再一个一个处理
    for(i = 0; i < cn; i++)
    {
        scanf("%d%d%d", &cons[i].from, &cons[i].to, &cons[i].dist);
        getchar();
        scanf("%c", &cons[i].dir);
    }
    scanf("%d", &qn);
    for(i = 0; i < qn; i++)
        scanf("%d%d%d", &querys[i].from, &querys[i].to, &querys[i].index);
    int curc = 0, curq = 0;
    //处理所有查询
    while(curq < qn)
    {
        //处理当前查询请求之前需要处理所有的其index之前的路
        while(curc <= querys[curq].index - 1)
        {
            int cx = 0, cy = 0;
            char dir = cons[curc].dir;
            if(dir == 'N') cy = cons[curc].dist;
            else if(dir == 'S') cy = -cons[curc].dist;
            else if(dir == 'E') cx = cons[curc].dist;
            else cx = -cons[curc].dist;
            //将两个农场所在的集合进行组合
            join(cons[curc].from, cons[curc].to, cx, cy);       
            curc++;
        }
        //处理查询请求
        int res = querySolve(querys[curq].from, querys[curq].to);
        printf("%d/n", res);
        curq++;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值