POJ1054 The Troublesome Frog
题目来源: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的更多相关文章
- IOI2002 POJ1054 The Troublesome Frog 讨厌的青蛙 (离散化+剪枝)
Description In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a we ...
- poj1054 The Troublesome Frog 瞎搞。
连接:http://poj.org/problem?id=1054 题意:就是一个格子里一条线上最长有几个青蛙(青蛙间隔相同)~.但是其实青蛙的起点重点必须是在外面. 直接写一个搜就是. #inclu ...
- POJ 1054 The Troublesome Frog
The Troublesome Frog Time Limit: 5000MS Memory Limit: 100000K Total Submissions: 9581 Accepted: 2883 ...
- (中等) POJ 1054 The Troublesome Frog,记忆化搜索。
Description In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a we ...
- The Troublesome Frog
In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a well-deserved ...
- POJ 1054 The Troublesome Frog(枚举+剪枝)
题目链接 题意 :给你r*c的一块稻田,每个点都种有水稻,青蛙们晚上会从水稻地里穿过并踩倒,确保青蛙的每次跳跃的长度相同,且路线是直线,给出n个青蛙的脚印点问存在大于等于3的最大青蛙走的连续的脚印个数 ...
- poj 1054 The Troublesome Frog (暴力搜索 + 剪枝优化)
题目链接 看到分类里是dp,结果想了半天,也没想出来,搜了一下题解,全是暴力! 不过剪枝很重要,下面我的代码 266ms. 题意: 在一个矩阵方格里面,青蛙在里面跳,但是青蛙每一步都是等长的跳, 从一 ...
- Poj 1054 The Troublesome Frog / OpenJudge 2812 恼人的青蛙
1.链接地址: http://poj.org/problem?id=1054 http://bailian.openjudge.cn/practice/2812 2.题目: 总时间限制: 10000m ...
- 【POJ】1054 The Troublesome Frog
题目是非常经典的搜索+剪枝.题意简言之就是,青蛙需要沿着直线踩着踏点通过田地,并且踏点需要至少为3.问哪条路径青蛙踩坏的作物最多.很好的一个条件是青蛙每次移动都是等间距的.题目需要注意将其排序. #i ...
随机推荐
- Linux 压缩文件 和解压文件
.zip 解压:unzip FileName.zip 压缩:zip FileName.zip DirName .rar 解压:rar -x FileName.zip 压缩:rar -a FileNam ...
- kernel下制作动态logo
kernel下制作动态logo 在uboot中实现logo的好处是反映速度快. 在kernel中实现logo的好处是,不管是android还是什么其他平台,logo显示无需考虑上层平台. 参照三星平台 ...
- setAttribute这个方法
setAttribute这个方法,在JSP内置对象session和request都有这个方法,这个方法作用就是保存数据,然后还可以用getAttribute方法来取出.比如现在又个User对象,Use ...
- SQL Server连接Oracle FAIL
第一步 安装好oracle客户端Oracle 11.2.0.3 Client Win 64-bit,并配置好TNS信息 第二步,配置odbc信息 配置好odbc连接信息,sql server肯定不能直 ...
- ViewPager的使用方法
首先是 导入jar包 下载地址:android-support-v4.jar 布局文件里添加viewPager布局 [html] view plaincopy <android.suppor ...
- 使用绘图API自定义组件
-----------------siwuxie095 工程名:CustomizeSwing 包名:com.siwuxie095.swi ...
- Unity3d 脚本与C#Socket服务器传输数据
Test.cs脚本 ------------------------------------------------------------------------------------------ ...
- DPDK内存管理(1)(转)
1 前言 DPDK将利用hugepage预留的物理内存统一的组织管理起来,然后以库的方式对外提供使用的接口.下图展示了DPDK中内存有关的模块的相互关系. rte_eal 是统一 ...
- JavaPersistenceWithMyBatis3笔记-第1章-001
一.介绍 1.项目结构 2.数据库结构 二.代码 1.Mapper package com.mybatis3.mappers; import java.util.List; import com.my ...
- 算法Sedgewick第四版-第1章基础-012一用stack实现输出一个数的二进制形式
@Test public void e1_3_5() { Stack<Integer> stack = new Stack<Integer>(); int N = 7; whi ...