【枚举】讨厌的青蛙,总踩我的稻田:( 谁最可恨?(POJ百练2812)

同样是《算法基础与在线实践》上的百练习题。先上例题:

 题目肯定是枚举相关,稍加思考就可以得到一个解决思路(笨比的我想了一天半):找到任意不相同的两点,当这两点是青蛙进入稻田中先踩的点时,根据两点确定一条直线找到方向和单步步长,进而找到这条直线在稻田上的所有点,再依次判断所有点是否是被踩过的存在的点。如果直线上所有点都满足,就得到了一条合理的青蛙的运动轨迹。

解决思路分析与代码实现

        最开始我的思路是开一个[5000][5000]的数组,枚举所有两个点的组合,先判断两点是否都存在(即被踩过),如果被踩过的话再去判断直线是否满足情况。显而易见这种方式时间复杂度太高。于是我将思路优化,从开始的“枚举所有两个点的组合”,变为“枚举所有存在的两个点的组合”。这里是很不相同的:前者是判断在5000*5000的地图上所有点的组合,而后者只考虑被记录的存在的点的组合。具体的操作,我没有抛弃这个5k*5k的数组而去选择一个结构体一维数组来储存。但我在二维数组上改变了原先对于(i,j)点就标记mapmap[i][j]为1的策略(mapmap是数组名),而是选择了将点存放在mapmap[i][x]中,令mapmap[i][x]=j。如下代码:

int tempmapx=0,tempmapy=0;
int maxhang=0;
for (int i = 1; i <= total; i++)
{
	scanf("%d %d",&tempmapx,&tempmapy);
	mapmap[tempmapx][0]++;
	mapmap[tempmapx][mapmap[tempmapx][0]]=tempmapy;
	if(tempmapx>maxhang)
    {
        maxhang=tempmapx;
    }
}

在mapmap[i][0]中储存第i行有多少个点,同时以i作为数组下标索引存储。通过mapmap[i][0]作为数量值,我们可以遍历第i行的所有点的存储数据。尽管在行内他们是无序的,但是行与行之间是有序的。比如对于测试数据:

 经过输入储存后,是这样的

 对于一个二维数组mapmap,我们的储存策略是将点的纵坐标存放到其横坐标行,但不排序。这样做有一个好处就是我们可以一次定位到想要找的点的所在行,然后用较少的时间找到该点。可以节省判断某点是否存在的时间。

        我们需要枚举两个不同的点,然后依据这两点的方向和步长找到所有在地图上的点。比如第一组点(2,1),(6,6),我们将第一个点作为起始点,得到的单步步长就是stepx=6-2=4,stepy=6-1=5。此时只要在第二个点上加上横纵步长,就可以得到下一个点的坐标为(10,11)。

        如何枚举两个不同的点可以用for循环来找到:每行包含点的数量都存放在了该行内[0]的位置。每层for循环的约束条件就是该值。如下:

for(int i=1;i<=maxhang;i++)//寻找第一个点(直线的起始点)
    {
        if(mapmap[i][0]!=0)//如果该行是有点存在的
        {
            for(int j=1;j<=mapmap[i][0];j++)//枚举该行内所有点,找到第一个坐标点(i,mapmap[i][j])
            {
                for(int a=i;a<=maxhang;a++)//寻找第二个点
                {
                    if(mapmap[a][0]!=0)//如果该行是有点存在的
                    {
                        int b=j+1;
                        if(a!=i)
                        {
                            b=1;
                        }        
                        //如果两点是在同一行,寻找第二个点应该从起始点的下一个点开始找。
                        //如果两点并不在同一行,寻找第二个点应该从当前行第一个点开始找。
                        for(;b<=mapmap[a][0];b++)//找到第二个坐标点(a,mapmap[a][b])
                        {
                            int stepx=a-i;//行步长
                            int stepy=mapmap[a][b]-mapmap[i][j];//列步长
                            int tempx;
                            int tempy;
                            //tempx,tempy作为一个暂时的点去遍历寻找是否存在
                            int templong=2;//目前正在判断的这条直线的长度
                            //判断点是否满足条件部分
                            //-------------------------

                        }
                    }
                }
            }
        }
    }

        接下来是判断点是否存在。不过在此之前,我们可以对代码进行优化。此时我们能确定直线上的所有点应该落在什么地方,但不是所有的情况我们都需要去考虑,去逐个验证点是否真的在那里。如果直线上所有的点加起来,数量也并没有超过我们当前已经确认的最大值,那么我们还判断他做什么?直接跳过!所以在找点之前,我们可以加上这段代码:

if(i+totlong*stepx>hang || i+totlong*stepx<=0)
{                                
    continue;
}

if(mapmap[i][j]+totlong*stepy>lie || mapmap[i][j]+totlong*stepy<=0)
{
    continue;
}

如果点是比目前最大值totlong要多的,那么即使只是多一个,这条直线的第totlong+1个点也一定是在地图范围(hang*lie)中的,而不可能出界。如果出界的话,别想了,直接下一位!

        做完这一步之后,我们再来判断点的存在情况。我的方法是按照当前步长方向,一步一步走。因为每行的所有点都被我们存放在了同一行,那么我们就可以根据要找的这个点的行数去找到那一行的所有点。如果找到了,这个点是存在的,那么让(tempx,tempy)再走一步到下一个点,直到出界为止;如果这个点不存在,这种情况不可行,那么我们需要再另外枚举两个点判断。

        但即使我们枚举的这条直线一直走出界了,他一定就满足条件吗?文章开头我们的思路就有说,我们找到的两个点是且一定是最开始走的两个点。也就是找一号点和二号点:

        对于这两种情况,我们可以在开始判断直线上每一个点是否存在前,先判断这条直线反方向上还是否有点可能存在。如果这个点在地图里面,那么这种情况我们可以直接跳过。因为这说明要么我们选取的两个点不是起始点和第二点;要么说明我们选取的起始点不满足情况。这样不仅避免了重复计算已经处理过的直线,更避免了将并不满足条件的情况计算进去的bug。

现在可以上程序总代码了:

#include<iostream>
#include<list>
#include<algorithm>
#include<vector>
#include<map>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>

using namespace std;

int mapmap[5005][5005]={0};

int main()
{
	int hang = 0, lie = 0,total=0;
	scanf("%d %d %d", &hang, &lie,&total);
	int tempmapx=0,tempmapy=0;
	int maxhang=0;
	for (int i = 1; i <= total; i++)
	{
		scanf("%d %d",&tempmapx,&tempmapy);
		mapmap[tempmapx][0]++;
		mapmap[tempmapx][mapmap[tempmapx][0]]=tempmapy;
		if(tempmapx>maxhang)
        {
            maxhang=tempmapx;
        }
	}
	int totlong = 0;//储存最长的线

	for(int i=1;i<=maxhang;i++)//寻找第一个点(直线的起始点)
    {
        if(mapmap[i][0]!=0)//如果该行是有点存在的
        {
            for(int j=1;j<=mapmap[i][0];j++)//枚举该行内所有点,找到第一个坐标点(i,mapmap[i][j])
            {
                for(int a=i;a<=maxhang;a++)//寻找第二个点
                {
                    if(mapmap[a][0]!=0)//如果该行是有点存在的
                    {
                        int b=j+1;
                        if(a!=i)
                        {
                            b=1;
                        }
                        //如果两点是在同一行,寻找第二个点应该从起始点的下一个点开始找。
                        //如果两点并不在同一行,寻找第二个点应该从当前行第一个点开始找。
                        for(;b<=mapmap[a][0];b++)//找到第二个坐标点(a,mapmap[a][b])
                        {
                            int stepx=a-i;//行步长
                            int stepy=mapmap[a][b]-mapmap[i][j];//列步长
                            int tempx=i-stepx;
                            int tempy=mapmap[i][j]-stepy;
                            //tempx,tempy作为一个暂时的点去遍历寻找是否存在
                            int templong=2;//目前正在判断的这条直线的长度

                            if(tempx>=1 && tempx<=hang && tempy>=1 && tempy<=lie)
                            {
                                continue;
                            }
                            //如果直线反方向前一个点是可能存在的,那么就要排除此情况

                            if(i+totlong*stepx>hang || i+totlong*stepx<=0)
                            {
                                continue;
                            }

                            if(mapmap[i][j]+totlong*stepy>lie || mapmap[i][j]+totlong*stepy<=0)
                            {
                                continue;
                            }
                            //当前直线长度比已经记录的最大值要小,故不需继续判断

                            tempx=a+stepx;
                            tempy=mapmap[a][b]+stepy;
                            //开始判断直线上所有点

                            while(tempx>=1 && tempx<=hang && tempy>=1 && tempy<=lie)//判断(tempx,tempy)是否存在
                            {
                                int flag=0;//用flag来记录是否找到点
                                for(int aa=1;aa<=mapmap[tempx][0];aa++)//在该行内查找
                                {
                                    if(mapmap[tempx][aa]==tempy)//存在
                                    {
                                        flag=1;
                                        templong++;//总长度+1
                                        tempx+=stepx;
                                        tempy+=stepy;//判断下一个点
                                        break;
                                    }
                                }
                                if(flag==0)//没找到,退出循环
                                {
                                    goto B;
                                }
                            }
                            if(templong>totlong)//当前情况要比已经记录的要大,更新数据
                            {
                                totlong=templong;
                            }
                            B:;
                        }
                    }
                }
            }
        }
    }

	if(totlong<3)//根据题意,小于3的情况要被排除
    {
        totlong=0;
    }
	printf("%d\n", totlong);
	return 0;
}

        感觉自己的方法好笨。。用最原始的方法费最大的气力解决最简单的问题。。。不过总算是解决了。。。还是需要多学习多磨练啊! 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值