跟关灯问题相似的地方是,第一眼都很懵逼,还是看了视频之后照着视频的思路过的;毕竟自己来的话效率奇低。
分析:
首先这是一道枚举问题。
枚举:就是从可能的解的集合中一一列举各个元素,判断其是否符合问题要求,符合的就是问题的解。
枚举问题的三个关键是:
一:给出解空间,建立简洁的数学模型。
二:减小解空间,即减小搜索的空间。
三:采用合适的搜索顺序
对于本题:
一:解空间是一条条线,每条线上有若干等距的点。数学模型:用dx,dy表示相邻点的x,y方向距离,则确定前两个点之后(确定了初始点与青蛙跳跃方向),再加上稻田的边界,就可知道这条线上有几个点。
二:首先最简单基本的枚举:枚举所有任意两点所成的线(其实数量级还是很大,第三步会进一步优化),求线上等距点。减小解空间的方法是:取点后,先判断一:其是不是“最开 始的两个点”(这一步具体实现要依赖于“三:采用合适的搜索顺序”),二:求出dx,dy,那么第二个点(x2,y2)加上(max-1)*dx,(max-1)*dy后,x和y是否已经越界(max是已经求出的中最长路径,初始为2,根据题目要求,如果最终结果max仍是二,max=0),如果已经越界,那么就直接考虑下一组点。
三: 对每一个点排序,x小的排在前面,x相等的话,y小的在前面。这样子的好处是:一,上文绿色高亮部分,在查找确定线上有几个点时,我们就可以进行二分查找,效率很高。另外,二:判断“两个点是不是线的起始点时”,我们只要按顺序取点即可,具体看代码(在代码高亮处,非常巧妙,仔细阅读):
#include <stdio.h>
#include <algorithm>
using namespace std;
int r,c,n;
struct plant{
int x,y;
};
plant plants[5001];
plant plantt;
bool operator < (const plant& a,const plant& b){
if(a.x == b.x)
return a.y<b.y;
return a.x<b.x;
}
int searchpath(plant secplant,int dx,int dy){ //确定前两个点之后,输入第二个点,求线上点数
int s;
plantt.x=secplant.x+dx;
plantt.y=secplant.y+dy;
s=2;
while(plantt.x<=r && plantt.x>=1 && plantt.y>=1 && plantt.y<=c){
if(!binary_search(plants,plants+n,plantt)){
s=0;
break;
}
plantt.x+=dx;
plantt.y+=dy;
s++;
}
return s;
}
int main() {
freopen("in.txt","r",stdin);
int dx,dy,px,py,max=2,steps;
scanf("%d %d",&r,&c);
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d %d",&plants[i].x,&plants[i].y);
sort(plants,plants+n);
for(int i=0;i<n-2;i++) //按顺序枚举
for(int l=i+1;l<n-1;l++){ //为什么是 l<n-1 我没想明白 (不过n也行的其实)
dx=plants[l].x-plants[i].x;
dy=plants[l].y-plants[i].y;
px=plants[i].x-dx;
py=plants[i].y-dy;
if(px>=1 && px<=r && py>=1 && py<=c) continue; //判断是不是“最开始的两个点”
if(plants[i].x+(max-1)*dx>r) break; //这一行多一个等号(dx>=r)就能毁灭世界
if((plants[i].y+(max-1)*dy)>c || (plants[i].y+(max-1)*dy)<1) continue; //与x方向是否越界的判断有很大不同,要注意
steps=searchpath(plants[l],dx,dy);
if(steps>max) max=steps;
}
if(max==2) max=0; printf("%d\n",max);
return 0;
}