POJ 1984 Navigation Nightmare(路径压缩并查集)

POJ 1984 Navigation Nightmare(路径压缩并查集)

http://poj.org/problem?id=1984

题意:

        有N个点在二维平面上,但是不直接给你他们的坐标,而是给你M条A B L D 语句表示从A到B有长为L的(方向为朝上或下或左或右的)直线。现在给你K个A B I 这样的询问,表示你需要回答在添加完上面前I条直线那一刻时,从A与B的曼哈顿距离(该值为|XA-XB|+|YA-YB|).

分析:

        首先本题应该先读入所有询问,然后按I从小到大离线处理每个询问的(代码中并未离线处理,因为本题测试数据的I就是默认从小到大增加的)。

        最左上角的坐标为(0,0)且当往右走时x坐标增大,往下走时y坐标增大。x为横坐标,y为纵坐标。定义并查集,令fa[i]表示i的父节点编号,X[i]与Y[i]表示从i走到它的父节点需要走多少X正方向与Y正方向的距离。

        所以对于在同一个连通分量的两个点A与B,我们才能算出他们的曼哈顿距离。如果不在同一个分量,那么两个点的相对坐标不知道,自然不能算曼哈顿距离了。
        比如第一句输入为 1 2 3 N 表示从1点朝北方向长3的路可以到2点,那么2的父亲就是1点,且x[2]=0,y[2]=-3,即2点的x坐标加0,y坐标加-3就是其父节点1点的x坐标和y坐标。

AC代码:157ms

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=40000+100;
const int UP=0,DOWN=1,LEFT=2,RIGHT=3;//上下左右 对应 北 南 西 东
const int dx[]= {0,0,-1,1};
const int dy[]= {-1,1,0,0};
int F[MAXN];//根
int x[MAXN];//x[i]=3,根的x坐标减i节点的x坐标之差为3
int y[MAXN];//同x
int n,m,k;
int road_1[MAXN],road_2[MAXN],road_3[MAXN],road_4[MAXN];//分别存输入路的4个变量
int findset(int i)
{
    if(F[i]==-1)return i;
    int temp = findset(F[i]);
    x[i] +=x[F[i]];//路径压缩成i到根的坐标差
    y[i] +=y[F[i]];
    return F[i]=temp;
}
void bind(int i,int j,int L,int D)//L为合并的路长,D为路的方向,合并的是i和j的根
{
    int fa = findset(i);
    int fb = findset(j);
    if(fa!=fb)//自己画个图推理一下,设i点的坐标为(0,0),然后推出j,fa,fb的坐标及相对关系
    {
        F[fa]=fb;
        x[fa] = L*dx[D]+x[j]-x[i];
        y[fa] = L*dy[D]+y[j]-y[i];
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(F,-1,sizeof(F));
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        for(int i=1; i<=m; i++)
        {
            char str[10];
            scanf("%d%d%d%s",&road_1[i],&road_2[i],&road_3[i],str);
            switch(str[0])
            {
            case 'N':
                road_4[i]=0;
                break;
            case 'S':
                road_4[i]=1;
                break;
            case 'W':
                road_4[i]=2;
                break;
            case 'E':
                road_4[i]=3;
                break;
            }
        }
        scanf("%d",&k);
        int i=0;//指针,保存当前处理到第几条road记录了,别忘了i要增长
        while(k--)//开始一条一条处理询问了
        {
            int u,v,time;
            scanf("%d%d%d",&u,&v,&time);
            for(int j=i+1; j<=time; j++)
            {
                int a,b,L,D;
                a=road_1[j];
                b=road_2[j];
                L=road_3[j];
                D=road_4[j];

                bind(a,b,L,D);//此处要合并a连通分量和b连通分量的根
            }
            int fu=findset(u);
            int fv=findset(v);
            if(fu!=fv)//不同连通分量
                printf("-1\n");
            else//相同连通分量
            {
                int dist = abs(x[u]-x[v])+abs(y[u]-y[v]);
                printf("%d\n",dist);
            }
            i=time;//注意
        }
    }
    return 0;
}
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: normal;">
</span></span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值