要求O(1)解决

image.png
100面值(c张),50面值(b张),10面值(a张),可乐单价×
原则(1):用户先用大面值,然后可乐售卖机找零。
原则(2):机器内找零的张数是无限的,优先找零大面值,不足大面值再用小面值的找零。
问题:假设买的可乐数量M瓶,用户要投钱几次?
要求O(1)解决

截图_20214427114444.png
截图_20215327115323.png

截图_20215327115354.png

截图_20215427115423.png

  • 我尽可能地去看100元面值(只能花100面值)的能帮我搞定多少瓶,(a瓶)
    • 我尽可能地去看50元面值(可以花100面值,50面值,但不能花10面值的)的能帮我搞定多少瓶,(b瓶)

。。。。。
只要任一时刻达到了m瓶,我就停,就求出了最小投币数
截图_20215627115646.png

麻烦的是啥? 之前的面值会留下小尾巴

截图_20215827115859.png
为什么当面面值买的第一瓶可乐如此特殊?
因为它有可能把它之前的小尾巴全部清掉

截图_20210327120301.png

  • 除法向上取整怎么搞最快?

image.png

  1. public static int putTimes(int m, int a, int b, int c, int x) {
  2. int[] qian = {100, 50, 10};
  3. int[] zhang = {c, b, a};
  4. //总共需要多少次投币
  5. int puts = 0;
  6. // 之前面值的钱还剩下多少总钱
  7. int preqianRest = 0;
  8. // 之前面值的钱还剩下多少总张数
  9. int preqianZhang = 0;
  10. for (int i = 0; i < 3 && m != 0; i++) {
  11. // 要用之前剩下的钱和当前面值的钱,共同买第一瓶可乐
  12. // 之所以之前的面值会剩下来,一定是剩下的钱,一直攒不出一瓶可乐的单价
  13. // 当前的面值付出一些钱 + 之前剩下的钱,此时有可能凑出一瓶可乐来
  14. // 那么当前面值参与搞定第一瓶可乐,需要掏出多少张呢? 就是curQianFirstBuyZhang
  15. int curQianFirstBuyZhang = (x - preqianRest + qian[i] - 1) / qian[i]; // 向上取整的快速方式
  16. if (zhang[i] >= curQianFirstBuyZhang) { // 如果之前的钱和当前面值的钱,能凑出第一瓶可乐
  17. // 凑出来了一瓶可乐也可能存在找钱的情况
  18. giveRest(qian, zhang, i + 1, (preqianRest + qian[i] * curQianFirstBuyZhang) - x, 1);
  19. puts += curQianFirstBuyZhang + preqianZhang;
  20. zhang[i] -= curQianFirstBuyZhang;
  21. m--;
  22. } else { // 之前的钱和当前面值的钱不能凑出第一瓶可乐
  23. preqianRest += qian[i] * zhang[i];
  24. preqianZhang += zhang[i];
  25. continue;
  26. }
  27. // 凑出第一瓶可乐,把之前的小尾巴清掉之后,当前面值有可能继续买更多的可乐,
  28. // 以下过程就是后续的可乐怎么用当前面值的钱来买
  29. // 用当前面值的钱买一瓶可乐需要多少张
  30. int curQianBuyOneColaZhang = (x + qian[i] - 1) / qian[i]; // 向上取整的快速方式
  31. // 用当前面值的钱,一共可以搞定几瓶可乐
  32. int curQianBuyColas = Math.min(zhang[i] / curQianBuyOneColaZhang, m);
  33. // 用当前面值的钱,每搞定一瓶可乐,售货机会吐出多少零钱
  34. int oneTimeRest = qian[i] * curQianBuyOneColaZhang - x;
  35. // 每次买一瓶可乐,吐出的找零总钱数是oneTimeRest
  36. // 一共买的可乐数是curQianBuyColas,所以把零钱去提升后面几种面值的硬币数,
  37. // 就是giveRest的含义
  38. giveRest(qian, zhang, i + 1, oneTimeRest, curQianBuyColas);
  39. // 当前面值去搞定可乐这件事,一共投了几次币
  40. puts += curQianBuyOneColaZhang * curQianBuyColas;
  41. // 还剩下多少瓶可乐需要去搞定,继续用后面的面值搞定去吧
  42. m -= curQianBuyColas;
  43. // 当前面值可能剩下若干张,要参与到后续买可乐的过程中去,
  44. // 所以要更新preQianRest和preQianZhang
  45. zhang[i] -= curQianBuyOneColaZhang * curQianBuyColas;
  46. preqianRest = qian[i] * zhang[i];
  47. preqianZhang = zhang[i];
  48. }
  49. return m == 0 ? puts : -1;
  50. }
  51. // 假设单次买可乐,每次零钱是oneTimeRest
  52. // 一共买了times次可乐,谁买的? i-1号面值买的
  53. // i... 可能获得零钱,请把张数调整对
  54. public static void giveRest(int[] qian, int[] zhang, int i, int oneTimeRest, int times) {
  55. for (; i < 3; i++) { // 3种面值
  56. zhang[i] += (oneTimeRest / qian[i]) * times;
  57. oneTimeRest %= qian[i];
  58. }
  59. }