POJ 2049 Finding Nemo

127 篇文章 0 订阅
7 篇文章 0 订阅

/*

做完这题我打算看《海底总动员》Demo Demo好可爱
用广搜过的, 主要思路如下:
(1)首先是建图, 由于输入给的都是线段, 但是我们平常处理这类问题都是转换为网格来做的, 因此需要
将线段转换为网格.转化的方法是对于每个格子,用其左上角点的坐标来表示这个格子,如果其左上角点的
坐标是[i][j],那么这个格子就表示为[i][j].将其四周边界的四条线段归这个格子管.即为每个格子建一
个数组round[i][j][4],第三维的四个位置分别表示这四条线段的类型: 0表示空气,1表示墙,2表示是一扇
门,这样这个模型就建立好了.
(2)其次是bfs的起始点选为Nemo所在的位置,注意要将这个浮点点位置转化为格子的坐标.转化的方法很简
单.对于x坐标取整即可,对于y坐标取整+1即可,比如Nemo所在位置为[1.2, 1.3]那么转化为格子的坐标即为:
[1, 2].这个坐标即位bfs遍历的起始点
(3)遍历的时候如果所走的方向是墙,则不可走.如果是门则将当前总的steps数+1,如果为空气,steps数不变.
另外一点就是如何判重.判重不能简单地记录有没有被访问过,而是需要记录到当前格子的最小步骤.如果当
前总步骤数小于当前格子的最小步骤数,则更新其最小步骤数并将这个格子加入队列中.
(4)遍历的终止位置即为题目中的出发位置[0, 0]
*/

#include <iostream>
#include <queue>
#define MAX_N 210 //最大限度边界

using namespace std;

int v[MAX_N + 1][MAX_N + 1];            //v[i][j]表示到格子[i][j]的最小步骤数
int round[MAX_N + 1][MAX_N + 1][4];     //记录当前格子四面边界的类型, 0:air 1:wall 2:door

int wn, dn, startXI, startYI, minSteps; //wn:墙的数目,dn:门的数目,起始点对应的格子坐标
double startXF, startYF;                //起始点的输入浮点坐标
                                       
int dirarray[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}}; //方向数组,走四个方向对坐标的变化
//上:0, 下:1, 左:2, 右:3

//入bfs队列的元素类型
struct elem
{
    //x, y记录这个格子的坐标; dir记录是从当前格子的哪个方向进入这个格子的,上:0, 下:1, 左:2, 右:3
    int x, y, dir, stepnum;
    //stepnum记录到达当前格子所需的步骤数
};

queue<elem> bfsq; //bfs的队列

//取当前方向的对面方向
void changeDir(int orgignal, int &newDir)
{
    if(orgignal == 0) newDir = 1;
    else if(orgignal == 1) newDir = 1;
    else if(orgignal == 2) newDir = 3;
    else newDir = 2;
}

//当断当前坐标是否在合法范围内
bool inRange(int x, int y)
{
    return x >= 0 && x <= 205 && y >= 0 && y <= 205;
}

void bfs()
{
    //将Demo的位置入队列作为bfs的起始位置
    while(!bfsq.empty()) bfsq.pop();
    elem curelem, newelem;
    curelem.x = startXI; curelem.y = startYI; curelem.dir = -1; curelem.stepnum = 0;
    v[startXI][startYI] = 0;
    bfsq.push(curelem);

    int curx, cury, curdir, cursteps, newx, newy, newdir, newsteps, d;
    while(!bfsq.empty())
    {
        curelem = bfsq.front();
        bfsq.pop();
       
        curx = curelem.x; cury = curelem.y; curdir = curelem.dir; cursteps = curelem.stepnum;

        //到达出发点
        if(curx == 0 && cury == 0)
        {
            //更新所需位置的最优值
            if(cursteps < minSteps)
                minSteps = cursteps;
            continue;   
        }

        //遍历当前格子的四个方向,尝试往这四个方向走
        for(d = 0; d < 4; d++)
        {
            //不能往回走
            if(d != curdir)
            {
                //所走方向不能是墙
                if(round[curx][cury][d] != 1)
                {
                    //得到新的格子坐标
                    newx = curx + dirarray[d][0];
                    newy = cury + dirarray[d][1];
                   
                    //新坐标在合法范围内
                    if(inRange(newx, newy))
                    {
                        //计算所有方向相对目标格子所在的方位
                        changeDir(d, newdir);

                        //门,步骤数+1
                        if(round[curx][cury][d] == 2)
                            newsteps = cursteps + 1;
                        else //空气,步骤数不变
                            newsteps = cursteps;

                        //判断这个新格子的新状态是否需要入队列
                        if((v[newx][newy] == 0xbf || newsteps < v[newx][newy]) && newsteps < minSteps)
                        {
                            v[newx][newy] = newsteps;
                            newelem.x = newx; newelem.y = newy; newelem.stepnum = newsteps; newelem.dir = newdir;
                            bfsq.push(newelem);
                        }
                    }
                }
            }
        }
    }
}

int main()
{
    int i, j, x, y, d, t;
    while(scanf("%d%d", &wn, &dn) && !(wn == -1 && dn == -1))
    {
        minSteps = INT_MAX;
        memset(v, 12, sizeof(v));
        memset(round, 0, sizeof(round));
        for(i = 1; i <= wn; i++)
        {
            scanf("%d%d%d%d", &x, &y, &d, &t);
            //输入的预处理,将线段(墙)转换为相应格子对应的四面边界
            if(d == 1)
                for(j = y + 1; j <= y + t; j++)
                    round[x][j][2] = round[x - 1][j][3] = 1;
            else
                for(j = x; j < x + t; j++)
                    round[j][y][0] = round[j][y + 1][1] = 1;
        }
        for(i = 1; i <= dn; i++)
        {
            scanf("%d%d%d", &x, &y, &d);
            //输入的预处理,将线段(门)转换为相应格子的四面边界方向
            if(d == 1)
                round[x][y + 1][2] = round[x - 1][y + 1][3] = 2;
            else
                round[x][y][0] = round[x][y + 1][1] = 2;
        }
        scanf("%lf%lf", &startXF, &startYF);
        //将Demo的位置转换为格子坐标
        startXI = startXF;
        startYI = startYF + 1;

        //题目中的异常数据
        if(startXI < 0 || startXI > 199 || startYI < 0 || startYI > 199)
            printf("0/n");
        else
        {
            bfs();
            if(minSteps == INT_MAX) printf("-1/n");
            else printf("%d/n", minSteps);
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值