首页 > 代码库 > POJ1696 Space Ant(贪心、向量叉乘、向量极角、线段相交、点在线段上)

POJ1696 Space Ant(贪心、向量叉乘、向量极角、线段相交、点在线段上)

题目链接:

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

题目描述:

Space Ant
 

Description

The most exciting space discovery occurred at the end of the 20th century. In 1999, scientists traced down an ant-like creature in the planet Y1999 and called it M11. It has only one eye on the left side of its head and just three feet all on the right side of its body and suffers from three walking limitations: 
  1. It can not turn right due to its special body structure. 
  2. It leaves a red path while walking. 
  3. It hates to pass over a previously red colored path, and never does that.

The pictures transmitted by the Discovery space ship depicts that plants in the Y1999 grow in special points on the planet. Analysis of several thousands of the pictures have resulted in discovering a magic coordinate system governing the grow points of the plants. In this coordinate system with x and y axes, no two plants share the same x or y
An M11 needs to eat exactly one plant in each day to stay alive. When it eats one plant, it remains there for the rest of the day with no move. Next day, it looks for another plant to go there and eat it. If it can not reach any other plant it dies by the end of the day. Notice that it can reach a plant in any distance. 
The problem is to find a path for an M11 to let it live longest. 
Input is a set of (x, y) coordinates of plants. Suppose A with the coordinates (xA, yA) is the plant with the least y-coordinate. M11 starts from point (0,yA) heading towards plant A. Notice that the solution path should not cross itself and all of the turns should be counter-clockwise. Also note that the solution may visit more than two plants located on a same straight line. 
技术分享

Input

The first line of the input is M, the number of test cases to be solved (1 <= M <= 10). For each test case, the first line is N, the number of plants in that test case (1 <= N <= 50), followed by N lines for each plant data. Each plant data consists of three integers: the first number is the unique plant index (1..N), followed by two positive integers x and y representing the coordinates of the plant. Plants are sorted by the increasing order on their indices in the input file. Suppose that the values of coordinates are at most 100.

Output

Output should have one separate line for the solution of each test case. A solution is the number of plants on the solution path, followed by the indices of visiting plants in the path in the order of their visits.

Sample Input

2
10
1 4 5
2 9 8
3 5 9
4 1 7
5 3 2
6 6 3
7 10 10
8 8 1
9 2 4
10 7 6
14
1 6 11
2 11 9
3 8 7
4 12 8
5 9 20
6 3 2
7 1 6
8 2 13
9 15 1
10 14 17
11 13 19
12 5 18
13 7 3
14 10 16

Sample Output

10 8 7 3 4 9 5 6 2 1 10
14 9 10 11 5 12 8 7 6 13 4 14 1 3 2

题目大意:

  一只生物吃东西……它不能右转,走路会留下痕迹,且不能穿过之前走过的痕迹

  现在有这么多食物,要从y轴上与你第一个到达的点的投影出发,问最多可以吃几个食物

思路:

  想象生物面朝的方向延伸出一条射线,射线顺时针方向的所有区域和路径的相交为你剩下可以吃到的区域(搜索区域)

  所以每次转向都在缩小你的搜索区域,转得角度越大,缩小得越厉害

  贪心的思想,每次选择剩下可以走的点(注意判断之前线段都不想交)中,转角最小的那个点,这样会保证搜索区域尽可能的大

  起始点选择最下面的点,因为假设选择不是最下面的点,总存在点(起始点之下)永远不在你的搜索区域中

 

代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cmath>
 6 using namespace std;
 7 
 8 const double EPS = 1e-10;    //精度系数
 9 const int INF = 0x3f3f3f3f;
10 const double PI = acos(-1.0);
11 const int N = 55;
12 
13 struct Point {
14     double x, y;
15     Point(double x = 0, double y = 0) :x(x), y(y) {}
16 };    //点的定义
17 
18 typedef Point Vector;    //向量的定义
19 
20 Vector operator - (Vector A, Vector B) { return Vector(A.x - B.x, A.y - B.y); }    //向量减法
21 
22 int dcmp(double x) {
23     if (fabs(x) < EPS)return 0; else return x < 0 ? -1 : 1;
24 }    //与0的关系
25 
26 double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }    //向量叉乘
27 double Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; }    //向量点乘
28 double Length(Vector A) { return sqrt(Dot(A, A)); }    //向量长度
29 
30 double Angle(Vector A, Vector B) {
31     double ans = atan2(A.y, A.x) - atan2(B.y, B.x);
32     return ans + (ans < 0 ? 2 * PI : 0);
33 }    //向量夹角( 范围[0, 2π) )
34 
35 bool SegmentProperIntersection(Point& a1, Point& a2, Point& b1, Point& b2) {
36     double c1 = Cross(a2 - a1, b1 - a1), c2 = Cross(a2 - a1, b2 - a1),
37         c3 = Cross(b2 - b1, a1 - b1), c4 = Cross(b2 - b1, a2 - b1);
38     return dcmp(c1)*dcmp(c2) < 0 && dcmp(c3)*dcmp(c4) < 0;
39 }    //判断两线段相交(恰有一个公共点且不在端点)
40 
41 bool OnSegment(Point& p, Point& a1, Point& a2) {
42     return dcmp(Cross(a1 - p, a2 - p)) == 0 && dcmp(Dot(a1 - p, a2 - p)) < 0;
43 }    //判断点是否在一条线段上(不含端点)
44 
45 int n, index[N], best, ans[N], res[N];
46 Point P[N];
47 bool vis[N];
48 
49 bool ok(int cur, int next, int now) {
50     Point last(0, P[res[0]].y);
51     for (int i = 0; i < cur; ++i) {
52         if (SegmentProperIntersection(last, P[res[i]], P[now], P[next])
53             || OnSegment(P[res[i]], P[now], P[next]))return false;
54         last = P[res[i]];
55     }
56     return true;
57 }
58 
59 void solve(int cur, int id, Vector v) {
60     double MIN = INF, L = INF;
61     int next = 0;
62     for (int i = 1; i <= n; ++i)if (!vis[i]) {    //如果没有访问过
63         double agl = Angle(P[i] - P[id], v);    //夹角
64         if (agl >= PI || !ok(cur, i, id))continue;    //保证方向,且与之前线段不相交
65         if (agl < MIN || (dcmp(agl - MIN) == 0 && Length(P[i] - P[id]) < L))    //扫描夹角最小的
66             MIN = agl, next = i, L = Length(P[i] - P[id]);
67     }
68     if (next) {
69         vis[next] = true;
70         res[cur] = next;
71         solve(cur + 1, next, P[next] - P[id]);
72         vis[next] = false;
73     } else if (cur > best) {    //不能再继续
74         best = cur;
75         memcpy(ans, res, sizeof(ans));
76     }
77 }
78 
79 int main() {
80     int t;
81     cin >> t;
82     while (t--) {
83         memset(vis, 0, sizeof(vis));
84         best = 0;
85         cin >> n;
86         double tmp = INF;
87         int m;
88         for (int i = 1; i <= n; ++i) {
89             scanf("%d%lf%lf", &index[i], &P[i].x, &P[i].y);
90             if (P[i].y < tmp) { tmp = P[i].y, m = i; }    //找到y坐标最小的点
91         }
92         vis[m] = true;
93         res[0] = m;
94         solve(1, m, Vector(P[m].x, 0));
95         printf("%d", best);
96         for (int i = 0; i < best; ++i)printf(" %d", index[ans[i]]);
97         printf("\n");
98     }
99 }

 

POJ1696 Space Ant(贪心、向量叉乘、向量极角、线段相交、点在线段上)