题组2

74 阅读7分钟

题目一

给定数组hard和money,长度都为N

hard[i]表示i号的难度, money[i]表示i号工作的收入

给定数组ability,长度都为M,ability[j]表示j号人的能力

每一号工作,都可以提供无数的岗位,难度和收入都一样

但是人的能力必须>=这份工作的难度,才能上班

返回一个长度为M的数组ans,ans[j]表示j号人能获得的最好收入

public static class Job {
   public int money;
   public int hard;

   public Job(int m, int h) {
      money = m;
      hard = h;
   }
}

public static class JobComparator implements Comparator<Job> {
   @Override
   public int compare(Job o1, Job o2) {
      return o1.hard != o2.hard ? (o1.hard - o2.hard) : (o2.money - o1.money);
   }
}

public static int[] getMoneys(Job[] job, int[] ability) {
   Arrays.sort(job, new JobComparator());
   // key : 难度   value:报酬
   TreeMap<Integer, Integer> map = new TreeMap<>();
   map.put(job[0].hard, job[0].money);
   // pre : 上一份进入map的工作
   Job pre = job[0];
   for (int i = 1; i < job.length; i++) {
      if (job[i].hard != pre.hard && job[i].money > pre.money) {
         pre = job[i];
         map.put(pre.hard, pre.money);
      }
   }
   int[] ans = new int[ability.length];
   for (int i = 0; i < ability.length; i++) {
      // ability[i] 当前人的能力 <= ability[i]  且离它最近的
      Integer key = map.floorKey(ability[i]);
      ans[i] = key != null ? map.get(key) : 0;
   }
   return ans;
}

题目二

贩卖机只支持硬币支付,且收退都只支持10 ,50,100三种面额

一次购买只能出一瓶可乐,且投钱和找零都遵循优先使用大钱的原则

需要购买的可乐数量是m,

其中手头拥有的10、50、100的数量分别为a、b、c

可乐的价格是x(x是10的倍数)

请计算出需要投入硬币次数?

// 正式的方法
// 要买的可乐数量,m
// 100元有a张
// 50元有b张
// 10元有c张
// 可乐单价x
public static int putTimes(int m, int a, int b, int c, int x) {
   //              0    1   2
   int[] qian = { 100, 50, 10 };
   int[] zhang = { c,  b,  a };
   // 总共需要多少次投币
   int puts = 0;
   // 之前面值的钱还剩下多少总钱数
   int preQianRest = 0;
   // 之前面值的钱还剩下多少总张数
   int preQianZhang = 0;
   for (int i = 0; i < 3 && m != 0; i++) {
      // 要用之前剩下的钱、当前面值的钱,共同买第一瓶可乐
      // 之前的面值剩下多少钱,是preQianRest
      // 之前的面值剩下多少张,是preQianZhang
      // 之所以之前的面值会剩下来,一定是剩下的钱,一直攒不出一瓶可乐的单价
      // 当前的面值付出一些钱+之前剩下的钱,此时有可能凑出一瓶可乐来
      // 那么当前面值参与搞定第一瓶可乐,需要掏出多少张呢?就是curQianFirstBuyZhang
      int curQianFirstBuyZhang = (x - preQianRest + qian[i] - 1) / qian[i];
      if (zhang[i] >= curQianFirstBuyZhang) { // 如果之前的钱和当前面值的钱,能凑出第一瓶可乐
         // 凑出来了一瓶可乐也可能存在找钱的情况,
         giveRest(qian, zhang, i + 1, (preQianRest + qian[i] * curQianFirstBuyZhang) - x, 1);
         puts += curQianFirstBuyZhang + preQianZhang;
         zhang[i] -= curQianFirstBuyZhang;
         m--;
      } else { // 如果之前的钱和当前面值的钱,不能凑出第一瓶可乐
         preQianRest += qian[i] * zhang[i];
         preQianZhang += zhang[i];
         continue;
      }
      // 凑出第一瓶可乐之后,当前的面值有可能能继续买更多的可乐
      // 以下过程就是后续的可乐怎么用当前面值的钱来买
      // 用当前面值的钱,买一瓶可乐需要几张
      int curQianBuyOneColaZhang = (x + qian[i] - 1) / qian[i];
      // 用当前面值的钱,一共可以搞定几瓶可乐
      int curQianBuyColas = Math.min(zhang[i] / curQianBuyOneColaZhang, m);
      // 用当前面值的钱,每搞定一瓶可乐,收货机会吐出多少零钱
      int oneTimeRest = qian[i] * curQianBuyOneColaZhang - x;
      // 每次买一瓶可乐,吐出的找零总钱数是oneTimeRest
      // 一共买的可乐数是curQianBuyColas,所以把零钱去提升后面几种面值的硬币数,
      // 就是giveRest的含义
      giveRest(qian, zhang, i + 1, oneTimeRest, curQianBuyColas);
      // 当前面值去搞定可乐这件事,一共投了几次币
      puts += curQianBuyOneColaZhang * curQianBuyColas;
      // 还剩下多少瓶可乐需要去搞定,继续用后面的面值搞定去吧
      m -= curQianBuyColas;
      // 当前面值可能剩下若干张,要参与到后续买可乐的过程中去,
      // 所以要更新preQianRest和preQianZhang
      zhang[i] -= curQianBuyOneColaZhang * curQianBuyColas;
      preQianRest = qian[i] * zhang[i];
      preQianZhang = zhang[i];
   }
   return m == 0 ? puts : -1;
}

public static void giveRest(int[] qian, int[] zhang, int i, int oneTimeRest, int times) {
   for (; i < 3; i++) {
      zhang[i] += (oneTimeRest / qian[i]) * times;
      oneTimeRest %= qian[i];
   }
}

题目三

已知一个消息流会不断地吐出整数 1~N,

但不一定按照顺序依次吐出

如果上次打印的序号为i, 那么当i+1出现时

请打印 i+1 及其之后接收过的并且连续的所有数

直到1~N全部接收并打印完

请设计这种接收并打印的结构

public static class Node {
   public String info;
   public Node next;

   public Node(String str) {
      info = str;
   }
}

public static class MessageBox {
   private HashMap<Integer, Node> headMap;
   private HashMap<Integer, Node> tailMap;
   private int waitPoint;

   public MessageBox() {
      headMap = new HashMap<Integer, Node>();
      tailMap = new HashMap<Integer, Node>();
      waitPoint = 1;
   }

   // 消息的编号,info消息的内容, 消息一定从1开始
   public void receive(int num, String info) {
      if (num < 1) {
         return;
      }
      Node cur = new Node(info);
      // num~num
      headMap.put(num, cur);
      tailMap.put(num, cur);
      // 建立了num~num这个连续区间的头和尾
      // 查询有没有某个连续区间以num-1结尾
      if (tailMap.containsKey(num - 1)) {
         tailMap.get(num - 1).next = cur;
         tailMap.remove(num - 1);
         headMap.remove(num);
      }
      // 查询有没有某个连续区间以num+1开头的
      if (headMap.containsKey(num + 1)) {
         cur.next = headMap.get(num + 1);
         tailMap.remove(num);
         headMap.remove(num + 1);
      }
      if (num == waitPoint) {
         print();
      }
   }

   private void print() {
      Node node = headMap.get(waitPoint);
      headMap.remove(waitPoint);
      while (node != null) {
         System.out.print(node.info + " ");
         node = node.next;
         waitPoint++;
      }
      tailMap.remove(waitPoint-1);
      System.out.println();
   }

}

题目四

现有司机N*2人,调度中心会将所有司机平分给A、B两个区域

第 i 个司机去A可得收入为income[i][0],

第 i 个司机去B可得收入为income[i][1],

返回所有调度方案中能使所有司机总收入最高的方案,是多少钱

// income -> N * 2 的矩阵 N是偶数!
// 0 [9, 13]
// 1 [45,60]
public static int maxMoney1(int[][] income) {
   if (income == null || income.length < 2 || (income.length & 1) != 0) {
      return 0;
   }
   int N = income.length; // 司机数量一定是偶数,所以才能平分,A N /2 B N/2
   int M = N >> 1; // M = N / 2 要去A区域的人
   return process1(income, 0, M);
}

// index.....所有的司机,往A和B区域分配!
// A区域还有rest个名额!
// 返回把index...司机,分配完,并且最终A和B区域同样多的情况下,index...这些司机,整体收入最大是多少!
public static int process1(int[][] income, int index, int rest) {
   if (index == income.length) {
      return 0;
   }
   // 还剩下司机!
   if (income.length - index == rest) {
      return income[index][0] + process1(income, index + 1, rest - 1);
   }
   if (rest == 0) {
      return income[index][1] + process1(income, index + 1, rest);
   }
   // 当前司机,可以去A,或者去B
   int p1 = income[index][0] + process1(income, index + 1, rest - 1);
   int p2 = income[index][1] + process1(income, index + 1, rest);
   return Math.max(p1, p2);
}

// 严格位置依赖的动态规划版本
public static int maxMoney2(int[][] income) {
   int N = income.length;
   int M = N >> 1;
   int[][] dp = new int[N + 1][M + 1];
   for (int i = N - 1; i >= 0; i--) {
      for (int j = 0; j <= M; j++) {
         if (N - i == j) {
            dp[i][j] = income[i][0] + dp[i + 1][j - 1];
         } else if (j == 0) {
            dp[i][j] = income[i][1] + dp[i + 1][j];
         } else {
            int p1 = income[i][0] + dp[i + 1][j - 1];
            int p2 = income[i][1] + dp[i + 1][j];
            dp[i][j] = Math.max(p1, p2);
         }
      }
   }
   return dp[0][M];
}

题目五

设计有setAll功能的哈希表

put、get、setAll方法,时间复杂度O(1)

public static class MyValue<V> {
   public V value;
   public long time;

   public MyValue(V v, long t) {
      value = v;
      time = t;
   }
}

public static class MyHashMap<K, V> {
   private HashMap<K, MyValue<V>> map;
   private long time;
   private MyValue<V> setAll;

   public MyHashMap() {
      map = new HashMap<>();
      time = 0;
      setAll = new MyValue<V>(null, -1);
   }

   public void put(K key, V value) {
      map.put(key, new MyValue<V>(value, time++));
   }

   public void setAll(V value) {
      setAll = new MyValue<V>(value, time++);
   }

   public V get(K key) {
      if (!map.containsKey(key)) {
         return null;
      }
      if (map.get(key).time > setAll.time) {
         return map.get(key).value;
      } else {
         return setAll.value;
      }
   }
}

题目六

leetcode.com/problems/sh… 给定一个数组arr,只能对arr中的一个子数组排序,

但是想让arr整体都有序

返回满足这一设定的子数组中,最短的是多长

public static int findUnsortedSubarray(int[] nums) {
   if (nums == null || nums.length < 2) {
      return 0;
   }
   int N = nums.length;
   int right = -1;
   int max = Integer.MIN_VALUE;
   for (int i = 0; i < N; i++) {
      if (max > nums[i]) {
         right = i;
      }
      max = Math.max(max, nums[i]);
   }
   int min = Integer.MAX_VALUE;
   int left = N;
   for (int i = N - 1; i >= 0; i--) {
      if (min < nums[i]) {
         left = i;
      }
      min = Math.min(min, nums[i]);
   }
   return Math.max(0, right - left + 1);
}