题目来源:http://poj.org/problem?id=1054

题目大意:

  有一种青蛙在晚上经过一片稻田,在庄稼上跳跃,会把庄稼压弯。这让农民很苦恼。我们希望通过分析青蛙跳跃的路径,找出对稻田造成最大损害的青蛙。青蛙跳跃时总是沿着直线并且步长均匀,但不同青蛙可能步长或方向不一样。如下图所示:

  稻田庄稼是均匀地种在矩形网格交点处的,如下左图所示。青蛙总是会完全穿过这片稻田,也就是说从稻田外跳入并最终跳出稻田。如下右图所示。

  有许多青蛙会穿过这片稻田,从庄稼上跳跃。我们可以知道那些庄稼被青蛙“踩”过,有的庄稼可能被多只青蛙踩过。但是我们并不知道青蛙的路径是怎样的。也就是说对于下左图的青蛙路径,我们只能知道下右图中的青蛙脚印点坐标。

  从Figure4,我们分析所有可能的青蛙路径,我们只关心脚印数大于3的路径,目标是找出所有可能的青蛙路径中脚印数最多的路径。Figure4对应的答案应该是7.

输入:第一行含两个整数R和C,表示稻田的行和列。1 <= R,C <= 5000.第二行为一个整数N,表示被压扁的庄稼数(即青蛙的脚印数),3 <= N <= 5000。接下来N行每行为一对整数,表示脚印坐标:行号r(1 <= r <= R)和列号c(1 <= c <= C),每个坐标只出现一次。

输出:一个整数表示穿过稻田留下脚印最多的青蛙的脚印数,如果找不到符合要求的青蛙路径输出0。


Sample Input

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

Sample Output

7

首先,暴力解决本题的思路是,遍历所有的脚印对,检查这两个脚印确定的直线可否穿过这对脚印形成青蛙路径,若可组成青蛙路径,记下路径的脚印数,然后找出路径上脚印数的最大值。这样做的问题是:一条路径上可能有很多点,那么每条路径都可能被计算多次,导致时间上的浪费。

可以用下面的方法来避免这个问题:我们假定青蛙在大方向上都是从稻田的一侧跳向另一侧,分别称为起点侧和终点侧。对于任意一条脚印路径,我们只在选取的脚印对(脚印对视为有序对,(i,j)和(j,i)为不同的脚印对)是路径上最靠近起点侧的两个脚印且i比j更靠近起点侧(或距离相等)时才检查一次这条路径(可以理解为 i 是青蛙跳进稻田第一步的脚印,j 是第二步的脚印),从而保证每条路径恰好被计算一次。

具体做法为:

首先,将所有脚印点进行排序。点的坐标(x, y), 排序策略为,首先按x由小到大排序,对于x相等的点再按列好从小到大排。

然后,对于排好序之后的脚印点,按顺序遍历脚印对时,假定第i个脚印点和第j个脚印点的坐标为(xi, yi), (xj, yj). 那么当 j > i 时,一定有xj >= xi。由前述的避免重复计算路径的策略,我们只计算所有的j>i的脚印对,即j一定是在经过i之后才到达的。

接下来,如何保证 i 和 j 恰好是最靠近起点侧的脚印(即青蛙跳进稻田后的第一步和第二步留下的脚印)?方法是:让青蛙“往回跳一步”,假定往回跳一步还在稻田内,那么有两种可能,一,回退一步的坐标是脚印点,那么这条直线可能形成路径,但不应该在遍历脚印点 i 和 j 时被计算,二,回退一步的坐标不是脚印点,那么这条直线不能穿过 i 和 j 形成青蛙路径,因为青蛙的步长是定值,无法直接从田外直接跳到 i。所以,只要青蛙回退一步的坐标仍然在田内,则这对脚印不可能符合要求,可以不用计算对应的路径。

再然后,并不是所有符合上面条件的 i 和 j 都会确定出一条可行的青蛙路径。(比如上面Figure4中的(2,2)和(3,4),所在的直线就不能组成可行路径)。判断方法就是假设路径可行,由已知的两个点已经知道青蛙的步长和方向,模拟青蛙向前跳,如果青蛙恰好沿脚印点跳出稻田,说明路径可行,否则不可行。

至此似乎已经可以比较好的解决这个这个问题了,不过还有一些优化的策略:

1.由于青蛙沿直线跳跃且一定会穿越整片稻田,那么脚印数一定有一个上界--稻田的长和宽中的较大值。若已经有某条路径达到了上界,则不需要再搜索和计算。

2.当确定一对要检查的脚印对 i 和 j 后,我们是假定这条路径可行,那么我们已经知道了假定的路径中青蛙的步长、方向和起点,所以我们可以预测出该条路径的脚印点数,如果脚印数小于已找到的最大值,则没有必要再检查这对点对应的路径了。

3.我们在排序时不一定要固定按行排或按列排。按长度方向较小的方向为主序来排序可以使排序发挥更大的作用。

按照上述的各种策略课将题目由TLE加快到100ms内完成。

最后要注意需要输出0的情况。

 ///////////////////////////////////////////////////////////
// POJ1054 The Troublesome Frog
// Memory: 172K Time: 63MS
// Language: C++ Result: Accepted
////////////////////////////////////////////////////////// #include <cstdio>
#include <cstdlib> using namespace std; struct Plant {
int x, y;
}; Plant foot_print[];
int r, c, n;
int max_steps = ; int cmp_x(const void * a, const void * b) {
Plant * pa = (Plant *) a;
Plant * pb = (Plant *) b;
if (pa->x == pb->x) {
return pa->y - pb->y;
} else {
return pa->x - pb->x;
}
} int cmp_y(const void * a, const void * b) {
Plant * pa = (Plant *) a;
Plant * pb = (Plant *) b;
if (pa->y == pb->y) {
return pa->x - pb->x;
} else {
return pa->y - pb->y;
}
} int check_x(int j, int dx, int dy) {
int ans = ;
Plant plant;
plant.x = foot_print[j].x + dx;
plant.y = foot_print[j].y + dy;
while (plant.x <= r && plant.y <= c && plant.y >= ) {
if (bsearch(&plant, foot_print, n, sizeof(Plant), cmp_x)) {
plant.x += dx;
plant.y += dy;
++ans;
} else {
return ;
}
}
return ans;
} int check_y(int j, int dx, int dy) {
int ans = ;
Plant plant;
plant.x = foot_print[j].x + dx;
plant.y = foot_print[j].y + dy;
while (plant.x >= && plant.x <= r && plant.y <= c) {
if (bsearch(&plant, foot_print, n, sizeof(Plant), cmp_y)) {
plant.x += dx;
plant.y += dy;
++ans;
} else {
return ;
}
}
return ans;
} int main(void) {
scanf("%d%d%d", &r, &c, &n);
for (int i = ; i < n; ++i) {
scanf("%d%d", &foot_print[i].x, &foot_print[i].y);
}
if (r > c) {
if (r > ) {
qsort(foot_print, n, sizeof(Plant), cmp_x);
for (int i = ; i < n - ; ++i) {
for (int j = i + ; j < n - ; ++j) {
int dx = foot_print[j].x - foot_print[i].x;
int dy = foot_print[j].y - foot_print[i].y;
int px = foot_print[i].x - dx;
int py = foot_print[i].y - dy;
if (px >= && py >= && py <= c) {
continue;
}
px = foot_print[i].x + max_steps * dx;
if (px > r) {
break;
}
py = foot_print[i].y + max_steps * dy;
if (py < || py > c) {
continue;
}
int ans = check_x(j, dx, dy);
if (ans > max_steps) {
max_steps = ans;
}
if (max_steps == r) {
printf("%d\n", max_steps);
return ;
}
}
}
}
} else if (c > ) {
qsort(foot_print, n, sizeof(Plant), cmp_y);
for (int i = ; i < n - ; ++i) {
for (int j = i + ; j < n - ; ++j) {
int dx = foot_print[j].x - foot_print[i].x;
int dy = foot_print[j].y - foot_print[i].y;
int px = foot_print[i].x - dx;
int py = foot_print[i].y - dy;
if (px >= && px <= r && py >= ) {
continue;
}
py = foot_print[i].y + max_steps * dy;
if (py > c) {
break;
}
px = foot_print[i].x + max_steps * dx;
if (px < || px > r) {
continue;
}
int ans = check_y(j, dx, dy);
if (ans > max_steps) {
max_steps = ans;
}
if (max_steps == c) {
printf("%d\n", max_steps);
return ;
}
}
}
}
if (max_steps <= ) {
max_steps = ;
}
printf("%d\n", max_steps);
return ;
}

POJ1054 The Troublesome Frog的更多相关文章

  1. IOI2002 POJ1054 The Troublesome Frog 讨厌的青蛙 (离散化+剪枝)

    Description In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a we ...

  2. poj1054 The Troublesome Frog 瞎搞。

    连接:http://poj.org/problem?id=1054 题意:就是一个格子里一条线上最长有几个青蛙(青蛙间隔相同)~.但是其实青蛙的起点重点必须是在外面. 直接写一个搜就是. #inclu ...

  3. POJ 1054 The Troublesome Frog

    The Troublesome Frog Time Limit: 5000MS Memory Limit: 100000K Total Submissions: 9581 Accepted: 2883 ...

  4. (中等) POJ 1054 The Troublesome Frog,记忆化搜索。

    Description In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a we ...

  5. The Troublesome Frog

    In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a well-deserved ...

  6. POJ 1054 The Troublesome Frog(枚举+剪枝)

    题目链接 题意 :给你r*c的一块稻田,每个点都种有水稻,青蛙们晚上会从水稻地里穿过并踩倒,确保青蛙的每次跳跃的长度相同,且路线是直线,给出n个青蛙的脚印点问存在大于等于3的最大青蛙走的连续的脚印个数 ...

  7. poj 1054 The Troublesome Frog (暴力搜索 + 剪枝优化)

    题目链接 看到分类里是dp,结果想了半天,也没想出来,搜了一下题解,全是暴力! 不过剪枝很重要,下面我的代码 266ms. 题意: 在一个矩阵方格里面,青蛙在里面跳,但是青蛙每一步都是等长的跳, 从一 ...

  8. Poj 1054 The Troublesome Frog / OpenJudge 2812 恼人的青蛙

    1.链接地址: http://poj.org/problem?id=1054 http://bailian.openjudge.cn/practice/2812 2.题目: 总时间限制: 10000m ...

  9. 【POJ】1054 The Troublesome Frog

    题目是非常经典的搜索+剪枝.题意简言之就是,青蛙需要沿着直线踩着踏点通过田地,并且踏点需要至少为3.问哪条路径青蛙踩坏的作物最多.很好的一个条件是青蛙每次移动都是等间距的.题目需要注意将其排序. #i ...

随机推荐

  1. GCD详细用法

    一.延迟执行 1.介绍 第一种方法,该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程. [self performSelector:@selector(run) withOb ...

  2. 【译】Android 数据库 ORMLite

    @DatabaseField cloumnName:指定字段名,不指定则变量名作为字段名  canBeNull:是否可以为null   dataType:指定字段的类型 defaultValue:指定 ...

  3. wait命令

    wait命令用来等待指令的指令,直到其执行完毕后返回终端.该指令常用于shell脚本编程中,待指定的指令执行完成后,才会继续执行后面的任务.该指令等待作业时,在作业标识号前必须添加备份号"% ...

  4. CSS布局奇淫巧计之-强大的负边距

    css中的负边距(negative margin)是布局中的一个常用技巧,只要运用得合理常常会有意想不到的效果.很多特殊的css布局方法都依赖于负边距,所以掌握它的用法对于前端的同学来说,那是必须的. ...

  5. 给Activity切换过程添加动画效果

    首先,在资源文件中定义一些动画效果 例如: <scale android:duration="@android:integer/config_mediumAnimTime" ...

  6. 关于A类,B类,C类IP地址的网段和主机数的计算方法

    关于A类,B类,C类IP地址的网段和主机数的计算方法 IP地址是一个32位的二进制数,由四个八位字段组成.每个IP地址包括两部分:一部分为网络标识(网络号),一部分为主机标识(主机号). A类地址前8 ...

  7. linux删除文件、创建文件

    1.删除文件 rm huahua.txt 2.创建文件 touch huahua.txt

  8. Java接口基础

    接口(interface) 1.接口体中包含常量的声明(没有变量)和抽象方法两部分.接口体中只有抽象方法,没有普通的方法,而且接口体中所有的常量访问权限一定是public,而且是static常量(允许 ...

  9. WPF之MVVM模式(2)

    我们都想追求完美 Every view in the app has an empty codebehind file, except for the standard boilerplate cod ...

  10. Fragment之间通过add切换时的显示与隐藏

    新手,不知道用什么方法实现 ,通过动态的方法显示了Fragment   A,在这个里面点击列表项时add方法动态加载Fragment  B,但是两者都会一起显示,重叠在一起了,如果用replace方法 ...