poj 2812 恼人的青蛙(枚举+剪枝)

程序设计实习枚举练习 poj 2812 恼人的青蛙(枚举+剪枝)
总时间限制: 2000ms 单个测试点时间限制: 500ms 内存限制: 65536kB

描述
在韩国,有一种小的青蛙。每到晚上,这种青蛙会跳越稻田,从而踩踏稻子。农民在早上看到被踩踏的稻子,希望找到造成最大损害的那只青蛙经过的路径。每只青蛙总是沿着一条直线跳越稻田,而且每次跳跃的距离都相同。

如下图所示,稻田里的稻子组成一个栅格,每棵稻子位于一个格点上。而青蛙总是从稻田的一侧跳进稻田,然后沿着某条直线穿越稻田,从另一侧跳出去
poj 2812 恼人的青蛙(枚举+剪枝)_第1张图片
如下图所示,可能会有多只青蛙从稻田穿越。青蛙的每一跳都恰好踩在一棵水稻上,将这棵水稻拍倒。有些水稻可能被多只青蛙踩踏。当然,农民所见到的是图4中的情形,并看不到图3中的直线,也见不到别人家田里被踩踏的水稻。
poj 2812 恼人的青蛙(枚举+剪枝)_第2张图片
poj 2812 恼人的青蛙(枚举+剪枝)_第3张图片
根据图4,农民能够构造出青蛙穿越稻田时的行走路径,并且只关心那些在穿越稻田时至少踩踏了3棵水稻的青蛙。因此,每条青蛙行走路径上至少包括3棵被踩踏的水稻。而在一条青蛙行走路径的直线上,也可能会有些被踩踏的水稻不属于该行走路径
①不是一条行走路径:只有两棵被踩踏的水稻;
②是一条行走路径,但不包括(2,6)上的水道;
③不是一条行走路径:虽然有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

来源
poj 1054

注意本题为了降低空间复杂度,不可以直接储存下地图,可以只存青蛙的位置,这样一来为了方便查找,必然要使用排序与二分查找,为了能使用库函数qsort()与bsearch(),必须编写函数指针compare(),以x作为第一关键字。
注意compare格式为(int*) (const void * e1,const void * e2 ),qsort()与bsearch()参数可以看IDE的提示。
而bsearch()返回值为void * 应该使用如下强制类型转化语句 p=(type *) bsearch()。
这就实现了二分查找,大大降低了查找代价,也保证了数据的有序性。
枚举前两步落脚点i,j。
接下来要考虑枚举顺序,为了减少一条路线重复被枚举,可以规定枚举顺序,如果反向一步没有踏出田野就停止枚举该情况。
再考虑提前终止不可能产生更优解的路线,具体为,该情况步长在当前最大值步数下是会出田野,提前终止这一情况,但是有一个细微(sophisticated)的点要考虑,就是如果出田野是因为x分量出边界,那么枚举以后的j对应的x步长只会越来越长,还会出界,故不必枚举接下来的j,直接跳到下一个i,可以break,反之如果因为y分量出边界,只能枚举以下的j,即只可以continue。
这个优化的思路充分利用了当前已知的最大值答案与储存枚举结构的单调性质,这个优化几乎使得枚举的第二个维度在一定计算后几乎退化,使得本来超时的算法能在20ms内AC,这个技巧看似平常优化力度不大但是效果显著,其中微妙之处值得回味。

384kB 20ms 1683 B G++

#define MAX_N 5000

#include<stdio.h>
#include<stdlib.h>

struct point_type
{
    int x,y;//1<=x<=r,1<=y<=c
};

int r,c,n,ans=2,dx,dy,k;
point_type point[MAX_N];
point_type *p=new point_type,*p_find=new point_type;

int compare(const void* e1,const void* e2)
{
    const point_type *p1=(const point_type*) e1;
    const point_type *p2=(const point_type*) e2;
    if (p1->x!=p2->x)
        return p1->x-p2->x;
    else
        return p1->y-p2->y;
}

inline bool is_inside(point_type* p)
{
    return (1<=p->x && p->x<=r && 1<=p->y && p->y<=c);
}

void enumerate()
{
    for (int i=0;i<n;i++)
        for (int j=i+1;j<n;j++)
        {
            //printf("%d %d\n",i,j);
            dx=(point+j)->x-(point+i)->x;
            dy=(point+j)->y-(point+i)->y;
            *p={(point+i)->x-dx,(point+i)->y-dy};
            if (is_inside(p))
                continue;
            *p={(point+i)->x+ans*dx,(point+i)->y+ans*dy};
            if (!is_inside(p))
            {
                *p={(point+i)->x+ans*dx,1};
                if (is_inside(p))
                    continue;
                else 
                    break;
            }
            for (k=2;;k++)
            {
                *p={(point+i)->x+k*dx,(point+i)->y+k*dy};
                p_find=(point_type*) bsearch(p,point,n,sizeof(point_type),compare);
                if (p_find==NULL)
                    break;
            }
            if (!is_inside(p) && k>ans)
                ans=k;
        }
    return;
}

int main()
{
    //freopen("input.txt","r",stdin); 
    scanf("%d%d%d",&r,&c,&n);
    for (int i=0;i<n;i++)
        scanf("%d%d",&(point+i)->x,&(point+i)->y); qsort(point,n,sizeof(point_type),compare); /*test for (int i=0;i<n;i++) printf("%d,%d\n",(point+i)->x,(point+i)->y); point_type* key=new point_type; *key={2,5}; point_type* p=(point_type*) bsearch(key,point,n,sizeof(point_type),compare); delete key; printf("%d\n",p-point); */ enumerate(); printf("%d\n",ans==2?0:ans); delete p; delete p_find; return 0; }

你可能感兴趣的:(poj 2812 恼人的青蛙(枚举+剪枝))