贪心+双指针 一船最多两人过河问题

155 阅读2分钟

题目

image.png

  • 本题最优解的贪心策略为:让小于船载重一半的体重尽量大的和大于船载重一半的体重尽量小的做一起

  • 双指针从小于等于船载重量一半的最右位置开始,向两边扩

  • 比如下例,假设船载重量为10,则一半为5

    • 先对数组进行排序,然后找到小于等于船载重量一半的最右位置,即下面最右边的5
    • 以5为left起点,7为right起点
    • 如果left+right>船的崽种,那么 left--,直到找到满足载重的最右位置的3
    • 3一只能和最右边的7满足载重,因为3左边一定是小于等于3的,所以left和right都能滑动2个位置
    • 此时最左边的3不能和9匹配,所以left滑动到1,1能和9匹配2个,因为左边只剩2个了,所以left和right滑动2个
    • 最终left滑动到边界后,船的数量为:能匹配的数量/2(左右部分2个人一起坐船的数量)+左边不能匹配的数量/2+右边匹配剩余的数量
  • 按照船载重的一半进行区分的原因

    • 如果单个重量大于一半,那么只能一人一船(右半部分剩余的就是只能一人一船的)
    • 如果重量都小于等于一半才能2人一船(左右部分能匹配的+左边部分剩余不能和右边匹配的)

image.png

function process(arr, limit) {
  let lessR = -1;
  arr = arr.sort((a, b) => a - b);

  // 所有数都比一半小,且最多只能两人一条船,那么只能两两组和
  if (arr[arr.length - 1] <= limit / 2) {
    return Math.ceil(arr.length / 2);
  }

  // 所有数都大于 limit 一半,那么只能每人一条船
  if (arr[0] > limit / 2) {
    return arr.length;
  }

  // 数组中存在 n <= limit/2 <= m 的情况

  let L = lessR,
    R = lessR + 1,
    // 左边不能和右边配对的个数,左边能配对的个数:L+1-lessUnused
    lessUnused = 0;
  // 查找最右且小于等于 limit/2 的位置
  for (let i = arr.length; i >= 0; i--) {
    if (arr[i] <= limit / 2) {
      lessR = i;
      break;
    }
    if (solved == 0) {
    }
  }

  while (L >= 0) {
    let solved = 0;
    // 在 L位置算出,R位置能够向右滑动配对多少个,左边同理,因为 L 是当前最大的位置,所以 L 左边一定能和 R 滑动的位置配对
    while (R < arr.length && arr[L] + arr[R] <= limit) {
      R++;
      solved++;
    }

    // L 位置不能和 R 位置配对,L 左滑
    if (solved == 0) {
      lessUnused--;
      L--;
    } else {
      // L 左边能配对,进行滑动并防止越界
      L = Math.max(-1, L - solved);
    }
  }

  // 左半边区域总数
  let lessAll = lessR + 1;

  // 左右能匹配的数量
  let lessUsed = lessAll - lessUnused;

  // 右边匹配完剩余的数量
  let rightUnused = arr.length - lessR - 1 - lessUsed;

  return lessUsed + Math.ceil((lessUnused + 1) / 2) + rightUnused;
}