首页 > 技术文章 > Saving James Bond - Easy Version

onlyblues 2021-04-10 19:05 原文

Saving James Bond - Easy Version

This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the world's most famous spy, was captured by a group of drug dealers. He was sent to a small piece of land at the center of a lake filled with crocodiles. There he performed the most daring action to escape -- he jumped onto the head of the nearest crocodile! Before the animal realized what was happening, James jumped again onto the next big head... Finally he reached the bank before the last crocodile could bite him (actually the stunt man was caught by the big mouth and barely escaped with his extra thick boot).

Assume that the lake is a 100 by 100 square one. Assume that the center of the lake is at (0,0) and the northeast corner at (50,50). The central island is a disk centered at (0,0) with the diameter of 15. A number of crocodiles are in the lake at various positions. Given the coordinates of each crocodile and the distance that James could jump, you must tell him whether or not he can escape.

Input Specification:

Each input file contains one test case. Each case starts with a line containing two positive integers N (≤ 100), the number of crocodiles, and D, the maximum distance that James could jump. Then N lines follow, each containing the (x,y) location of a crocodile. Note that no two crocodiles are staying at the same position.

Output Specification:

For each test case, print in a line "Yes" if James can escape, or "No" if not.

Sample Input 1:

14 20
25 -15
-25 28
8 49
29 15
-35 -2
5 28
27 -29
-8 -28
-20 -35
-25 -20
-13 29
-30 15
-35 40
12 12

Sample Output 1:

Yes

Sample Input 2:

4 13
-12 12
12 12
-12 -12
12 -12

Sample Output 2:

No

 

解题思路

  这道题目有两种不同的思路。一种是用并查集,另外一种是用DFS。

并查集

  就是把每两个点进行一次比较,如果他们对应的圆是相切或者是相交,就把他们合并到同一个集合。同时我们还要另外设两个集合,一个是用来存放一开始与原点对应范围相切或者是相交的圆所对应的点。另外一个集合是用来存放与边界相切或者是相交的圆所对应的点。最后比较一下这两个集合的代表元素是否相同,也就是这两个集合是否属于同一个集合。如果最后这两个集合的代表元素相同,说明找到了逃生路线;如果最后这两个集合的代表元素不同,就没有找到逃生路线。

  AC代码如下:

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <iostream>
 4 
 5 struct Point {
 6     int x, y;
 7 };
 8 
 9 void merge(int *set, int a, int b);
10 int findRoot(int *set, int x);
11 
12 int main() {
13     int n, dist;                        // n是鳄鱼的数量,dist是可以跳跃的距离 
14     scanf("%d %d", &n, &dist);
15     int set[n + 2];                     // 多出两个位置,其中set[n]用来存放与原点范围相邻的点,set[n+1]用来存放与边界相邻的点 
16     std::fill(set, set + n + 2, -1);    // 初始化set,每一个元素为-1,代表每一个点都是一个独立的集合 
17     
18     Point point[n];
19     for (int i = 0; i < n; i++) {
20         scanf("%d %d", &point[i].x, &point[i].y);
21         // 如果这个点对应的圆与原点范围相切或相交,就把这个点合并到集合set[n]中 
22         if (point[i].x * point[i].x + point[i].y * point[i].y <= (7.5 + dist) * (7.5 + dist)) {
23             merge(set, i, n);
24         }
25         // 如果这个点对应的圆与边界相切或相交,就把这个点合并到集合set[n+1]中
26         if (50 - abs(point[i].x) <= dist || 50 - abs(point[i].y) <= dist) {
27             merge(set, i, n + 1);
28         }
29     }
30     
31     // 每两个点进行一次比较检测 
32     for (int i = 0; i < n; i++) {
33         for (int j = i + 1; j < n; j++) {
34             // 如果这两个点对应的圆相切或相交,就把这两个点合并到同一个集合中 
35             if ((point[i].x - point[j].x) * (point[i].x - point[j].x) + (point[i].y - point[j].y) * (point[i].y - point[j].y) <= dist * dist) {
36                 merge(set, i, j);
37             }
38         }
39     }
40     
41     // 最后判断set[n]和set[n+1]这两个集合的代表元素是否相同,如果相同说明这两个集合已合并为同一个集合,有逃生路径,否则没有 
42     printf("%s", findRoot(set, n) == findRoot(set, n + 1) ? "Yes" : "No");
43     
44     return 0;
45 }
46 
47 // 按秩归并。按规模大小,把规模小的集合并到规模大的集合 
48 void merge(int *set, int a, int b) {
49     int root1 = findRoot(set, a), root2 = findRoot(set, b);
50     if (root1 != root2) {
51         if (set[root1] < set[root2]) {
52             set[root1] += set[root2];
53             set[root2] = root1;
54         }
55         else {
56             set[root1] += set[root2];
57             set[root2] = root1;
58         }
59     }
60 }
61 
62 // 查找,同时路径压缩 
63 int findRoot(int *set, int x) {
64     return set[x] < 0 ? x : (set[x] = findRoot(set, set[x]));
65 }

DFS

  就是从原点出发,先找到第一个可以跳跃的点,然后从这个点开始深度遍历,找到符合的点,也就是找到能够相切或相交的圆对应的点,然后继续DFS。如果发现某一个点对应的圆与边界相切或相交,就说明有逃生路径,退出DFS。否则把剩余的点进行遍历,来找逃生路径。

  AC代码如下:

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <iostream>
 4 
 5 struct Point {
 6     int x, y;
 7 };
 8 
 9 int n, dist;    // n是鳄鱼的数量,dist是可以跳跃的距离,定义为全局变量 
10 
11 bool isSave(Point *point, bool *vis);
12 int firstJumpPos(Point *point, bool *vis);
13 bool isJump(Point *point, int i, int j);
14 bool DFS(Point *point, bool *vis, int src);
15 
16 int main() {
17     scanf("%d %d", &n, &dist);
18     bool vis[n] = {false};
19     Point point[n];
20     for (int i = 0; i < n; i++) {
21         scanf("%d %d", &point[i].x, &point[i].y);
22     }
23     
24     printf("%s", isSave(point, vis) ? "Yes" : "No");    // 返回true代表可以获救,否则不可以获救 
25     
26     return 0;
27 }
28 
29 bool isSave(Point *point, bool *vis) {
30     bool ret = false;       // ret代表是否可以获救,true可以,false,不可以 
31     while (ret == false) {  // 当还没有找到逃生路径时,进入循环来找逃生路径 
32         int pos = firstJumpPos(point, vis); // 找到第一个可以逃生的点,返回这个点对应的数组下标位置 
33         if (pos == -1) break;               // 如果返回-1说明已经把可以第一次跳跃的点都找一遍了,已经没有逃生路径了,退出循环 
34         
35         ret = DFS(point, vis, pos);         // 否则对这个点进行深度遍历,返回是否有逃生路径 
36     }
37     
38     return ret;
39 }
40 
41 int firstJumpPos(Point *point, bool *vis) {
42     for (int i = 0; i < n; i++) {
43         // 如果这个点没有被访问过,并且对应的圆与原点范围相切或相交,就回这个点对应的数组下标位置 
44         if (!vis[i] && point[i].x * point[i].x + point[i].y * point[i].y <= (7.5 + dist) * (7.5 + dist)) return i;
45     }
46     
47     return -1;
48 }
49 
50 bool isJump(Point *point, int i, int j) {
51     // 判断这两个点对应的圆是否相切或相交 
52     return (point[i].x - point[j].x) * (point[i].x - point[j].x) + (point[i].y - point[j].y) * (point[i].y - point[j].y) <= dist * dist;
53 }
54 
55 bool DFS(Point *point, bool *vis, int src) {
56     bool ret = false;
57     vis[src] = true;
58     // 如果这个点与边界相切或相交,就说明找到逃生路径了,返回true 
59     if (50 - abs(point[src].x) <= dist || 50 - abs(point[src].y) <= dist) ret = true; 
60     for (int i = 0; i < n && ret == false; i++) {   // 否则继续找
61         if (!vis[i] && isJump(point, src, i)) {     // 如果这个点没有被访问并且可以从前一个点跳到这个点,也就是与前一个点对应的圆相切或相交 
62             ret = DFS(point, vis, i);               // 就从这个点进行深度遍历,继续找逃生路径 
63         }
64     }
65     
66     return ret;
67 }

推荐阅读