LeetCode874 模拟行走机器人

理解本题的题意很关键,机器人在(0,0)点开始行走,如果(0,0)点有障碍怎么办,这种情况是不管它,开始下一步行走,一旦机器人离开(0,0)点,这个点的障碍物才生效,后面如果回到此点则不能跨过此障碍。也就是说我们需要先走一步,再去判断这一步是否有效,有效则更新坐标,否则原地不动,继续下一次动作。至于右转和左转实际就是改变机器人的朝向,起初机器人向北,右转则朝向东,左转则朝向西。不同的朝向,意味着向前一步改变的坐标形式不一样,如果朝北,则x不动,y递增;如果朝南,则x不动,y递减;如果朝西,则x递减,y不动;如果朝东,则x递增,y不动;也就是在每次实施除了左转右转的动作(调整朝向)之外的移动操作时,需要知道机器人的朝向。知道了朝向就往前走呗,遇到了前方障碍物则不要往前,结束此次动作,开始下一次动作。

  难点是怎么确定朝向,有个笨办法是用一个整数表示此方向,1代表北方,-1代表南方,2代表东方,-2代表西方。当方向标识的绝对值等于1时,前进动作改变的是y坐标;当方向标识的绝对值等于2时,前进动作改变的是x坐标。当方向标识大于0时,每次向前前进1位;当方向标识小于0时,每次后退1位;还有一个问题是,确定朝向,我们可以把分别在东西南北时右转左转的方向写好,如果每次动作是转动(值<0)时直接调用方向选择常量确定本次转动后的朝向。这就叫方法一吧。

方法二,优化方向的表示,如果用坐标来表示方向不仅可以很好的确定方向之间的关系,而且还能确定此方向上的增量。考虑[0,1],[1,0],[0,-1],[-1,0]分别代表北、东,南,西。可以看到每个坐标的左边就是它的左转方向,右边的就是它的右边方向,也就是说,给一个方向i(方向向量的索引),则左转的方向是i-1,右转的方向是i+1。等等,那两头呢,开始的位置不能减1,末端的位置不能加1。把首和尾连接器起来就好了,因此要进行取余操作,长度为4。当然了(0-1)%4没意义,因此改写成((0-1)+4)%4,即左转为(i+3)%4,这样对中间位置和起始位置都适用。方向确定好了。那坐标增量呢,注意到某一方向上行进一步的增量恰好就是该方向的坐标。以北为例,y方向增量是1,x方向增量是0,也就是(0,1)。这种方法可以大大减少代码量。

但是这种方法有冗余操作,在不需要更新x或者y时还是会进行无用的赋值。

 

方法二:

class Solution {
public:
    int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
       vector<pair<int,int>> dire={{0,1},{1,0},{0,-1},{-1,0}};
       set<pair<int,int>> ob;
       int i=0,x=0,y=0,maxdis=0;
       for(vector<int> vi:obstacles){
           ob.insert(make_pair(vi[0],vi[1]));
       }
       for(int cd:commands){
           if(cd==-2){//左转
               i=(i+3)%4;
           }else if(cd==-1){//右转
               i=(i+1)%4;
           }else{//在某一个方向上对x或者对y更改
               while(cd){
                   int dx=x+dire[i].first;
                   int dy=y+dire[i].second;
                   if(ob.find(make_pair(dx,dy))==ob.end()){//没有障碍物则更新x和y
                      x=dx;
                      y=dy;
                     maxdis=max(maxdis,x*x+y*y);
                    }else{
                      break;
                   }
                   --cd;
               }
           }
       }
     
      return  maxdis;
    }
};

方法一 (笨办法)

class Solution {
public:
    int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
        int flag=1;  // 1向北,2向东,-1向南,-2向西
         //0左转变3,0右转变1
         map<pair<int, int>,int> hash;
         set<pair<int, int>> ob;
        // {1,-2}表示北左转,re表示转后的朝向
        vector<pair<int,int>> vep={{1,-2},{1,-1},{2,-2},{2,-1},{-2,-2},{-2,-1},{-1,-2},{-1,-1}};
        vector<int>re={-2,2,1,-1,-1,1,2,-2};
        for(int i=0;i<re.size();i++){
           hash[vep[i]]=re[i];
        }
        for(int i=0;i<obstacles.size();i++)
        {
           ob.insert(make_pair(obstacles[i][0],obstacles[i][1]));
        }
        int x=0,y=0;
        int step;
        int distance=0;
        for(int step:commands)
        {
            int sym=flag>0?1:-1;
            if(step>0){
                if(abs(flag)==1){
                    while(step>0){
                        int ds=y+sym;
                        if(ob.find(make_pair(x,ds))==ob.end()){
                            y=ds;
                            distance=max(distance,x*x+y*y);
                        }else{
                          break;
                        }
                       --step;
                    }
                }else{
                     while(step>0){
                         int ds=x+sym;
                        if(ob.find(make_pair(ds,y))==ob.end()){
                            x=ds;
                            distance=max(distance,x*x+y*y);
                        }else{
                          break;
                       }
                       --step
                    }
                }
            }else{//改变方向
                flag= hash[make_pair(flag,step)];
            }
        }
          
      return distance;
    }
};

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值