2812:恼人的青蛙,考点:枚举顺序和剪枝
原题:http://bailian.openjudge.cn/practice/2812/
描述
在韩国,有一种小的青蛙。每到晚上,这种青蛙会跳越稻田,从而踩踏稻子。农民在早上看到被踩踏的稻子,希望找到造成最大损害的那只青蛙经过的路径。每只青蛙总是沿着一条直线跳越稻田,而且每次跳跃的距离都相同。
如下图所示,稻田里的稻子组成一个栅格,每棵稻子位于一个格点上。而青蛙总是从稻田的一侧跳进稻田,然后沿着某条直线穿越稻田,从另一侧跳出去。如下图所示,可能会有多只青蛙从稻田穿越。青蛙的每一跳都恰好踩在一棵水稻上,将这棵水稻拍倒。有些水稻可能被多只青蛙踩踏。当然,农民所见到的是图4中的情形,并看不到图3中的直线,也见不到别人家田里被踩踏的水稻。
请你写一个程序,确定:在一条青蛙行走路径中,最多有多少颗水稻被踩踏。例如,图4的答案是7,因为第6行上全部水稻恰好构成一条青蛙行走路径。
输入
从标准输入设备上读入数据。第一行上两个整数R、C,分别表示稻田中水稻的行数和列数,1≤R、C≤5000。第二行是一个整数N,表示被踩踏的水稻数量, 3≤N≤5000。在剩下的N行中,每行有两个整数,分别是一颗被踩踏水稻的行号(1~R)和列号(1~C),两个整数用一个空格隔开。而且,每棵被踩踏水稻只被列出一次。
输出
从标准输出设备上输出一个整数。如果在稻田中存在青蛙行走路径,则输出包含最多水稻的青蛙行走路径中的水稻数量,否则输出0。
样例输入
6 7 14 2 1 6 6 4 2 2 5 2 6 2 7 3 4 6 1 6 2 2 3 6 3 6 4 6 5 6 7
样例输出
7
解法
思路:能想到枚举的起点也是很不容易的(个人觉得),枚举路径上的前两个点。
这道题的关键还在于剪枝,
- 判断前两点的合法性(能否从外面一步跳到第一个点)
- 有序,并非随机枚举,步长小的优先考虑,所以要排序,这里超级重要,我自己肯定想不到。
- 剪枝,最多也跳不到现在的最大值的就可以直接否定。
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <vector> 5 using namespace std; 6 bool field[5001][5001]; 7 int N; 8 int R, C; 9 class Plant { 10 public: 11 int x, y; 12 Plant(int inx,int iny):x(inx),y(iny){} 13 bool operator<(const Plant&p) { 14 if (x == p.x) 15 return y < p.y; 16 else 17 return x < p.x; 18 } 19 }; 20 int maxSteps(Plant A, int dx, int dy) { 21 Plant temp(A.x + dx, A.y + dy); 22 int steps = 2;//至少要跳两步 23 while (temp.x <= R && temp.x >= 1 && temp.y <= C && temp.y >= 1) { 24 if (!field[temp.x][temp.y]) 25 return 0;//不构成一条路径 26 temp.x += dx; 27 temp.y += dy; 28 steps++; 29 } 30 return steps; 31 } 32 int main() { 33 memset(field, 0, sizeof(field)); 34 vector<Plant>Plants; 35 int maxjump = 2; 36 cin >> R >> C; 37 int N; 38 cin >> N; 39 for(int i=0;i<N;i++){ 40 int x, y; 41 cin >> x >> y; 42 field[x][y] = 1; 43 Plants.push_back(Plant(x, y)); 44 } 45 sort(Plants.begin(), Plants.end()); 46 for(int i=0;i<N-2;i++) 47 for (int j = i + 1; j < N - 1; j++) { 48 int dx = Plants[j].x - Plants[i].x; 49 int dy = Plants[j].y - Plants[i].y; 50 int beginx = Plants[i].x - dx;//一开始的点:应该在稻田外,第一步是从稻田外跳进来 51 int beginy = Plants[i].y - dy; 52 if (beginx >= 1 && beginx <= R && beginy >= 1 && beginy <= C) 53 continue;//第一点的前一点在稻田内,说明步长小了,应该重新选第二个点 54 if (Plants[i].x + (maxjump - 1)*dx > R) 55 break;//x方向过界,出发点不合理 56 if (Plants[i].y + (maxjump - 1)*dy > C || Plants[i].y + (maxjump - 1)*dy < 1) 57 continue;//y方向过界,重新选择第二个点 58 int steps = maxSteps(Plants[j], dx, dy);//这样跳最多能跳的步数 59 if (steps > maxjump) 60 maxjump = steps; 61 } 62 if (maxjump == 2) 63 maxjump = 0; 64 cout << maxjump << endl; 65 return 0; 66 }