POJ 1984 Navigation Nightmare 并查集

题目链接:http://poj.org/problem?id=1984

Farmer John's pastoral neighborhood has N farms (2 <= N <= 40,000), usually numbered/labeled 1..N. A series of M (1 <= M < 40,000) vertical and horizontal roads each of varying lengths (1 <= length <= 1000) connect the farms. A map of these farms might look something like the illustration below in which farms are labeled F1..F7 for clarity and lengths between connected farms are shown as (n):
	F1 --- (13) ---- F6 --- (9) ----- F3

            |                                 |

           (3)                                |

            |                                (7)

           F4 --- (20) -------- F2            |

            |                                 |

           (2)                               F5

            | 

           F7 

Being an ASCII diagram, it is not precisely to scale, of course.

Each farm can connect directly to at most four other farms via roads that lead exactly north, south, east, and/or west. Moreover, farms are only located at the endpoints of roads, and some farm can be found at every endpoint of every road. No two roads cross, and precisely one path
(sequence of roads) links every pair of farms.

FJ lost his paper copy of the farm map and he wants to reconstruct it from backup information on his computer. This data contains lines like the following, one for every road:

There is a road of length 10 running north from Farm #23 to Farm #17
There is a road of length 7 running east from Farm #1 to Farm #17
...

As FJ is retrieving this data, he is occasionally interrupted by questions such as the following that he receives from his navigationally-challenged neighbor, farmer Bob:

What is the Manhattan distance between farms #1 and #23?

FJ answers Bob, when he can (sometimes he doesn't yet have enough data yet). In the example above, the answer would be 17, since Bob wants to know the "Manhattan" distance between the pair of farms.
The Manhattan distance between two points (x1,y1) and (x2,y2) is just |x1-x2| + |y1-y2| (which is the distance a taxicab in a large city must travel over city streets in a perfect grid to connect two x,y points).

When Bob asks about a particular pair of farms, FJ might not yet have enough information to deduce the distance between them; in this case, FJ apologizes profusely and replies with "-1".
Input
	* Line 1: Two space-separated integers: N and M



* Lines 2..M+1: Each line contains four space-separated entities, F1,

        F2, L, and D that describe a road. F1 and F2 are numbers of

        two farms connected by a road, L is its length, and D is a

        character that is either 'N', 'E', 'S', or 'W' giving the

        direction of the road from F1 to F2.



* Line M+2: A single integer, K (1 <= K <= 10,000), the number of FB's

        queries



* Lines M+3..M+K+2: Each line corresponds to a query from Farmer Bob

        and contains three space-separated integers: F1, F2, and I. F1

        and F2 are numbers of the two farms in the query and I is the

        index (1 <= I <= M) in the data after which Bob asks the

        query. Data index 1 is on line 2 of the input data, and so on.
Output
	* Lines 1..K: One integer per line, the response to each of Bob's

        queries.  Each line should contain either a distance

        measurement or -1, if it is impossible to determine the

        appropriate distance.
Sample Input
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6 1
1 4 3
2 6 6
Sample Output
13
-1
10
Hint
At time 1, FJ knows the distance between 1 and 6 is 13.
At time 3, the distance between 1 and 4 is still unknown.
At the end, location 6 is 3 units west and 7 north of 2, so the distance is 10.

    这题用了两天时间去思考,同时在刚写代码的时候参考了https://www.cnblogs.com/kuangbin/archive/2013/04/05/3000475.html的代码,感谢!

    A有n个农场,农场之间有m条路,之后输入f1,f2,L,d,农场编号、距离、方向(之后称为第一组数据输入)。注意到这里的方向都是垂直或者水平的,所以后面在记录坐标的时候只需要改变x/y一个方向的就好。之后B问了k个问题,输入数据(之后称为第二组数据)f1,f2,是农场编号,I意思在在A数据输入的第I行知不知道f1,f2之间的距离,这里距离题目也给了定义是|x1-x2|+|y1-y2|,如果可以知道则输出距离,不知道就输出-1。以下给出逐步解题的思考过程。

    在第一次写这个题目的时候第一个感觉是混乱,没有一个明确的思路,数据结构怎么用也不明晰。只有逐步写下去了。第一组数据输入的时候首先要记录下这个图的信息,建立数组f1,f2,L,和 char 数组d存储。之后可以想到在输出最后距离的时候判断是否输出-1,就要用并查集分组,如果在一组可以得到距离则输出距离。因此需要pre[],并查集,并初始化pre[i]=i;.B问了k个问题,要把他们当做一个一个的问题处理,用一个node记录信息除了x,y,I之外,还需要记录id表示顺序,以便在输出时按id顺序输出答案。至此,所需数据结构基本搭建完。

核心解题:在将所有输入数据获取完毕后

step1:首先要将问题按照I的大小排序,小的在前面,使用sort和cmp函数。

step2:之后一个一个处理k个问题(for循环)。处理一个问题就需要把第一组数据拿来建图获得答案,并且这个问题的I有多大就到第一组数据的第几行,处理第一组数据时从第一行开始,用t标记。寻找第t组数据的两个农场的根节点。

step3:!!写并查集find函数,递归找根节点,同时将这个农场的坐标记录下来,是以根节点为原点的dx【x】+=dx【pre【x】】

重点:不能写成dx[x]+=dx[temp],因为temp永远是这个节点的最终的根节点,而pre【x】只是当前这个节点最近的一个根节点,而距离的更改需要根据离当前节点的最近的那个节点去修改。

step4:距离更新

如果两个根节点不一样则需要合并,永远把第一个农场作为第二个农场的根节点。重点写一下距离如何更新

dy[t2] = dy[f1[t]] - dy[f2[t]] + l[t]; 

因为这个时候t2已经变成以t1为根节点了,那么自然坐标要改变,首先回到子节点f2[t]的坐标,所以 - dy[f2[t]],再从f2[t]走到f1[t],这里根据(WSEN跟新),然后再从f1[t]走到t1,即f1[t]与它根节点的距离即+ dy[f1[t]]

最后输出答案。

总的来说,这题值得好好体会思考!

附代码:

#include
#include
#include
using namespace std;
const int maxn = 40010;
int f1[maxn], f2[maxn], l[maxn];
int pre[maxn];
int dx[maxn], dy[maxn];
char d[maxn][3];
struct Node {
    int x;
    int y;
    int I;
    int id;
};
Node node[maxn];
int ans[maxn];
bool cmp(Node a,Node b)
{
    return a.I < b.I;             //输入的第几行不一定就是默认的升序,可能是乱序的需要我们排序
}
int find(int x)
{
    if(x==pre[x])
    {
        return x;
    }
    int temp = find(pre[x]);
    //dx【x】是x的坐标,要相对于不同的根节点更新
    //路劲压缩
    dx[x] +=  dx[pre[x]];              //错误原因。。。写成了dx[x]+=dx[x]+dx[pre[x]];
    dy[x] +=  dy[pre[x]];               //不能写dx+=dx[temp]!!!!!!!!!!
    return pre[x]=temp;
}

int main()
{
    int n, m,k;
    cin >> n >> m;                    //n个农场,m个路
    //init
    for (int i = 1; i <= n; i++)
    {
        pre[i] = i;
    }
    memset(dx, 0, sizeof(dx));
    memset(dy, 0, sizeof(dy));
    for (int i = 1; i <= m; i++)
    {
        cin >> f1[i] >> f2[i] >> l[i] >> d[i];
    }
    cin >> k;
    for (int i = 0; i < k; i++)      //x是第一个农场,y是第二个,不是坐标
    {
        cin >> node[i].x >> node[i].y >> node[i].I;
        node[i].id = i;
    }
    sort(node , node + k , cmp);
    //进行数据处理,根据问的问题进行
    int t = 1;         //用于从第一部分输入的第一个元素开始处理坐标值
    for (int i = 0; i < k; i++)        //处理的是问题输入部分
    {
        //在把问题输入一行的结果得出后,才会进入下一行,而在这个处理这一行时要得出答案,遍历所有while中条件
        while (t <= m&&node[i].I >= t)//只有I标记大于tt时候说明需要知道之前的所有路径
        {
            int t1 = find(f1[t]), t2 = find(f2[t]);
            if (t1 != t2) //根节点不一样,需要更新操作,总是把前面的作为根节点
            {
                pre[t2] = t1;
                if (d[t][0] == 'N') //yu坐标改变,+
                {
                    dy[t2] = dy[f1[t]] - dy[f2[t]] + l[t];           //根节点t2的坐标
                dx[t2] = dx[f1[t]] - dx[f2[t]];    
                }
                else if (d[t][0] == 'S')
                {
                    dy[t2] = dy[f1[t]] - dy[f2[t]] - l[t];
                    dx[t2] = dx[f1[t]] - dx[f2[t]];        
                }
                else if (d[t][0] == 'E')
                {
                    dx[t2] = dx[f1[t]] - dx[f2[t]] + l[t];
                    dy[t2] = dy[f1[t]] - dy[f2[t]];    
                }
                else if (d[t][0] == 'W') // f1  在  f2    的左边
                {
                    dx[t2] = dx[f1[t]] - dx[f2[t]] - l[t];
                    dy[t2] = dy[f1[t]] - dy[f2[t]];    
                }
            }
            t++;
        }//while结束,在第t列第一部分数据的情况下,将各个点坐标值更新
         //输出结果
        if (find(node[i].x) != find(node[i].y))ans[node[i].id] = -1;
        else
        {
            ans[node[i].id]= abs(dx[node[i].x] - dx[node[i].y]) + abs(dy[node[i].x] - dy[node[i].y]);     //node中的xy代表田地的编号!!!!!
        }
        
    }
    for (int i = 0; i < k; i++)
    {
        cout << ans[i] << endl;
    }
    return 0;

}

勇往直前,自我管理,怀着一颗奋斗的心,永不退怯!

你可能感兴趣的:(ACM)