特殊二分总结:shopee二面算法题(又是该死的二分

最近面了shopee新加坡office,在二面中遇到了一道有意思的题目。
题目分为两部分,开始比较简单,followup会有难度(但是也是之前遇到过总结过的二分,但是该死的又是没有自己想出来

题目:
给出一个升序数组,其中每个数字代表的是每个空位置的坐标。现在要给三个人安排位置,要求使他们之间距离最大(以相邻两个人距离中较小的距离为距离)
解法:
首先肯定要把一个安排在最左边,一个在最右边(贪心,这时面试官问了我为什么要这样,怎么证明这样是对的?)
然后只需要找出中间的人的位置就行了。
最简单可以遍历一遍就可以了,但是我们可以用二分:如果左边范围比右边大,接下来可以在左边找;或者如果左边范围比右边小,之后在右边找。
但是需要注意的是:即使对于一个mid值它的两边不相等,但是这个mid位置仍然可能是最后的位置,所以二分的时候要注意不能简单的和以前一样:lo = mid + 1 or hi = mid - 1,而应该是:lo = mid or hi = mid,最后的范围会找出两个位置,再在这两个位置中找。(一开始写错了,幸好自己检查出来了,改过来了)

int minDist(vector<int>& seats) {
  int sz = seats.size();
  if (sz < 3)
  	return -1;
  int lo = 0, hi = sz-1;
  int mid;
  while (lo < hi-1) {
  	mid = lo + (hi - lo) / 2;
    int leftDist = seats[mid] - seats[0];
    int rightDist = seats[sz-1] - seats[mid];
    if (leftDist < rightDist)
    	lo = mid;
    else if (leftDist > rightDist)
    	hi = mid;
    else {
    	lo = mid;
      hi = mid;
      break;
    }
  }
  return max(min(seats[lo]-seats[0], seats[sz-1]-seats[lo]), min(seats[hi]-seats[0], seats[sz-1]-seats[hi]));
}

followup:
如果有k个人来安排座位,使最小距离最大,怎么做?
一开始是蒙蔽的,第一反应是dp,dp[i][j]:前i个作为给j个人,距离是多少?发现根本不行,因为给出一个范围,必然有两个人在最左最右,状态之间根本不可以转移。
其实自己也想起来了二分,但是自己想的还是按照之前做法的二分,发现根本写不出来。这时候没法了。
在面试官的提醒之下才想起来直接对范围做二分,看每个大小是否符合要求就行了。(自己突然想起来的时候想抽死自己,自己明明做过类似的题目很多了,但还是不能够自己想出来)
具体做法:
最小0,最大为最左和最右位置的距离,对它们做二分,对每个距离大小,以两个人之间的距离大于等于该距离为前提给人安排座位,看看能不能把人安排进去。如果可以,说明最终结果可能更大;如果不可以,说明结果必须更小。

另外,问了时间复杂度,自己又算错了。
自己想也没有想就说的是nlogn,n是seats数组的长度。但是考虑到二分的是椅子间的最大距离,所以应该是nlogd,d是最大距离。

也许从面试官角度来说,followup在提示的情况下能够想出来可能不错了(lc上medium-hard级别),但是我自己知道做过类似题目,总结过,还是不能自己想出来,对自己智商和记忆力感到失望(也许用最近没怎么刷题来安慰自己了)

————————————————————————————————

这里是最重要的,为什么类似这种二分的题目总是想不起来应该怎么做?(自己之前刷leetcode的时候也发现了,这类题目自己总是想不起来应该怎么做)这是一个很严重的问题,因为这个方法一旦想不起来就基本上没有其他办法来做,要是出现在以后其他公司的面试中的话会比较严重,所以最好这一次完全搞定,确保之后遇到类似题目能够一次搞定。
类似的题目之前有猴子吃香蕉还有船运货物之类的。

特殊二分指的是:直接对所求东西的范围进行二分。

最关键的是:
1.明确自己想求的东西的范围(可以模糊一些,范围更大一些,可以很大都没关系的)
2.直接对这个范围二分,看看可不可以(也就是能不能找到一个条件来让我们知道该怎么缩小范围)

为什么经常想不起来这个方法呢?
按照平时解题的思路来说,直接对所求东西做二分看起来是有点不可思议的。
它的最小最大范围,包括最重要的–需要满足的某个条件,都是不明显的,都是需要拐一个弯才能够想得到的

那么以后怎么使用这个方法呢?
题目中出现“最小”或者“最大”,想到用这里的二分。
有些一下子看不出解法的题目,都用这里的思路(直接对所求的东西来二分)试一试(自己在面对一下子想不出解法的题目,最常想到的是dp,以后也要注意一下二分)。
如果不刻意去用二分,真的很难想到

类似题目有什么相似点:
可能最重要的一点是:题目中都有求“最大”或者“最小”的要求。
1.有可以得到的大致的范围。
2.对于某个条件,范围中一边可以,一边不可以。
3.以上两点都不明显,需要自己刻意用二分然后看看能不能找到这两个条件

posted @ 2019-11-08 14:59  于老师的父亲王老爷子  阅读(119)  评论(0编辑  收藏  举报