image.png
只能说大起之后必有大落,不过t4最后还是自己做出来的。

5259. 计算应缴税款总额

给你一个下标从 0 开始的二维整数数组 brackets ,其中 brackets[i] = [upperi, percenti] ,表示第 i 个税级的上限是 upperi ,征收的税率为 percenti 。税级按上限 从低到高排序(在满足 0 < i < brackets.length 的前提下,upperi-1 < upperi)。
税款计算方式如下:

  • 不超过 upper0 的收入按税率 percent0 缴纳
  • 接着 upper1 - upper0 的部分按税率 percent1 缴纳
  • 然后 upper2 - upper1 的部分按税率 percent2 缴纳
  • 以此类推

给你一个整数 income 表示你的总收入。返回你需要缴纳的税款总额。与标准答案误差不超 10-5 的结果将被视作正确答案。

示例 1:
输入:brackets = [[3,50],[7,10],[12,25]], income = 10 输出:2.65000 解释: 前 $3 的税率为 50% 。需要支付税款 $3 50% = $1.50 。 接下来 $7 - $3 = $4 的税率为 10% 。需要支付税款 $4 10% = $0.40 。 最后 $10 - $7 = $3 的税率为 25% 。需要支付税款 $3 25% = $0.75 。 需要支付的税款总计 $1.50 + $0.40 + $0.75 = $2.65 。
示例 2:
输入:brackets = [[1,0],[4,25],[5,50]], income = 2 输出:0.25000 解释: 前 $1 的税率为 0% 。需要支付税款 $1
0% = $0 。 剩下 $1 的税率为 25% 。需要支付税款 $1 25% = $0.25 。 需要支付的税款总计 $0 + $0.25 = $0.25 。
示例 3:
输入:brackets = [[2,50]], income = 0 输出:0.00000 *解释:
没有收入,无需纳税,需要支付的税款总计 $0 。

提示:

  • 1 <= brackets.length <= 100
  • 1 <= upperi <= 1000
  • 0 <= percenti <= 100
  • 0 <= income <= 1000
  • upperi 按递增顺序排列
  • upperi 中的所有值 互不相同
  • 最后一个税级的上限大于等于 income

思路:
按要求计算即可

  1. class Solution {
  2. public double calculateTax(int[][] b, int s) {
  3. int p = 0;
  4. int x = 0;
  5. double res = 0;
  6. for (int[] v : b) {
  7. if (s >= v[0]) {
  8. res += 1.0 * (v[0] - x) * (v[1] / 100.0);
  9. x = v[0];
  10. } else {
  11. p = v[1];
  12. break;
  13. }
  14. }
  15. res += 1.0 * (s - x) * (p / 100.0);
  16. return res;
  17. }
  18. }

5270. 网格中的最小路径代价

给你一个下标从 0 开始的整数矩阵 grid ,矩阵大小为 m x n ,由从 0 到 m n - 1 的不同整数组成。你可以在此矩阵中,从一个单元格移动到 下一行 的任何其他单元格。如果你位于单元格 (x, y) ,且满足 x < m - 1 ,你可以移动到 (x + 1, 0), (x + 1, 1), …, (x + 1, n - 1) 中的任何一个单元格。注意: 在最后一行中的单元格不能触发移动。
每次可能的移动都需要付出对应的代价,代价用一个下标从 0 开始的二维数组 moveCost 表示,该数组大小为 (m
n) x n ,其中 moveCost[i][j] 是从值为 i 的单元格移动到下一行第 j 列单元格的代价。从 grid 最后一行的单元格移动的代价可以忽略。
grid 一条路径的代价是:所有路径经过的单元格的 值之和 加上 所有移动的 代价之和 。从 第一行 任意单元格出发,返回到达 最后一行 任意单元格的最小路径代价

示例 1:
image.png
输入:grid = [[5,3],[4,0],[2,1]], moveCost = [[9,8],[1,5],[10,12],[18,6],[2,4],[14,3]] 输出:17 解释:最小代价的路径是 5 -> 0 -> 1 。 - 路径途经单元格值之和 5 + 0 + 1 = 6 。 - 从 5 移动到 0 的代价为 3 。 - 从 0 移动到 1 的代价为 8 。 路径总代价为 6 + 3 + 8 = 17 。
示例 2:
输入:grid = [[5,1,2],[4,0,3]], moveCost = [[12,10,15],[20,23,8],[21,7,1],[8,1,13],[9,10,25],[5,3,2]] 输出:6 解释: 最小代价的路径是 2 -> 3 。 - 路径途经单元格值之和 2 + 3 = 5 。 - 从 2 移动到 3 的代价为 1 。 路径总代价为 5 + 1 = 6 。

提示:

  • m == grid.length
  • n == grid[i].length
  • 2 <= m, n <= 50
  • grid 由从 0 到 m * n - 1 的不同整数组成
  • moveCost.length == m * n
  • moveCost[i].length == n
  • 1 <= moveCost[i][j] <= 100

思路:
线性DP

  1. class Solution {
  2. public int minPathCost(int[][] g, int[][] moveCost) {
  3. int n = g.length, m = g[0].length;
  4. int[][] f = new int[n][m];
  5. for (int i = 0; i < m; i++)
  6. f[0][i] = g[0][i];
  7. for (int i = 1; i < n; i++)
  8. Arrays.fill(f[i], 0x3f3f3f3f);
  9. for (int i = 0; i < n - 1; i++) {
  10. for (int j = 0; j < m; j++) {
  11. int x = g[i][j];
  12. for (int k = 0; k < m; k++) {
  13. f[i + 1][k] = Math.min(f[i + 1][k], f[i][j] + moveCost[x][k] + g[i + 1][k]);
  14. }
  15. }
  16. }
  17. int min = 0x3f3f3f3f;
  18. for (int i = 0; i < m; i++)
  19. min = Math.min(min, f[n - 1][i]);
  20. return min;
  21. }
  22. }

5289. 公平分发饼干

给你一个整数数组 cookies ,其中 cookies[i] 表示在第 i 个零食包中的饼干数量。另给你一个整数 k 表示等待分发零食包的孩子数量,所有 零食包都需要分发。在同一个零食包中的所有饼干都必须分发给同一个孩子,不能分开。
分发的 不公平程度 定义为单个孩子在分发过程中能够获得饼干的最大总数。
返回所有分发的最小不公平程度。

示例 1:
输入:cookies = [8,15,10,20,8], k = 2 输出:31 解释:一种最优方案是 [8,15,8] 和 [10,20] 。 - 第 1 个孩子分到 [8,15,8] ,总计 8 + 15 + 8 = 31 块饼干。 - 第 2 个孩子分到 [10,20] ,总计 10 + 20 = 30 块饼干。 分发的不公平程度为 max(31,30) = 31 。 可以证明不存在不公平程度小于 31 的分发方案。
示例 2:
输入:cookies = [6,1,3,2,2,4,1,2], k = 3 输出:7 解释:一种最优方案是 [6,1]、[3,2,2] 和 [4,1,2] 。 - 第 1 个孩子分到 [6,1] ,总计 6 + 1 = 7 块饼干。 - 第 2 个孩子分到 [3,2,2] ,总计 3 + 2 + 2 = 7 块饼干。 - 第 3 个孩子分到 [4,1,2] ,总计 4 + 1 + 2 = 7 块饼干。 分发的不公平程度为 max(7,7,7) = 7 。 可以证明不存在不公平程度小于 7 的分发方案。

提示:

  • 2 <= cookies.length <= 8
  • 1 <= cookies[i] <= 105
  • 2 <= k <= cookies.length

思路:
方法1:回溯
方法2:状态压缩DP
第一种:考虑所有物品的子集x,枚举x的所有子集,计算将x分给k个人的最小最大代价
第二种:考虑前k个人,瓜分所有饼干的最小最大代价
方法3:二分+ 状态压缩
二分最大代价(左界 = 饼干数量的最大值, 右界 = 饼干的和)
二分代码块内计算该瓜分所需的最小人数,由此判断下一步动作

  1. class Solution {
  2. int[] sum = new int[8];
  3. int k;
  4. int[] c;
  5. int ans = 0x3f3f3f3f;
  6. public int distributeCookies(int[] c, int k) {
  7. this.c = c;
  8. this.k = k;
  9. dfs(0);
  10. return ans;
  11. }
  12. void dfs(int u) {
  13. if (u == c.length) {
  14. int max = 0;
  15. for (int i = 0; i < k; i++)
  16. max = Math.max(max, sum[i]);
  17. ans = Math.min(ans, max);
  18. return;
  19. }
  20. for (int i = 0; i < k; i++) {
  21. sum[i] += c[u];
  22. dfs(u + 1);
  23. sum[i] -= c[u];
  24. }
  25. }
  26. }
  27. // 回溯+ 剪枝
  28. class Solution {
  29. int[] c;
  30. int k;
  31. int[] s = new int[8];
  32. public int distributeCookies(int[] c, int k) {
  33. this.c = c;
  34. this.k = k;
  35. Arrays.sort(c);
  36. int max = 0, sum = 0;
  37. for (int x : c) {
  38. max = Math.max(x, max);
  39. sum += x;
  40. }
  41. int l = max, r = sum;
  42. while (l < r) {
  43. int mid = l + r >> 1;
  44. Arrays.fill(s, 0);
  45. if (dfs(c.length - 1, mid))
  46. r = mid;
  47. else
  48. l = mid + 1;
  49. }
  50. return l;
  51. }
  52. boolean dfs(int u, int t) {
  53. if (u < 0) {
  54. return true;
  55. }
  56. Set<Integer> set = new HashSet<>();
  57. for (int i = 0; i < k; i++) {
  58. if (s[i] + c[u] > t || set.contains(s[i])) continue;
  59. set.add(s[i]);
  60. s[i] += c[u];
  61. if (dfs(u - 1, t))
  62. return true;
  63. s[i] -= c[u];
  64. if (s[i] == 0 || s[i] + c[u] == t)
  65. break;
  66. }
  67. return false;
  68. }
  69. }
  1. // 状压DP
  2. // 从物品角度
  3. class Solution {
  4. public int distributeCookies(int[] cookies, int K) {
  5. int n = cookies.length;
  6. int[][] f = new int[1 << n][K + 1];
  7. for (int i = 0; i < 1 << n; i++) Arrays.fill(f[i], 0x3f3f3f3f);
  8. f[0][0] = 0;
  9. for (int i = 0; i < 1 << n; i++) {
  10. for (int k = 1; k <= K; k++) {
  11. for (int st = i; st > 0; st = i & (st - 1)) {
  12. int t = 0;
  13. for (int j = 0; j < n; j++)
  14. if ((st >> j & 1) == 1)
  15. t += cookies[j];
  16. f[i][k] = Math.min(f[i][k], Math.max(f[i ^ st][k - 1], t));
  17. }
  18. f[i][k] = Math.min(f[i][k], f[i][k - 1]);
  19. }
  20. }
  21. return f[(1 << n) - 1][K];
  22. }
  23. }
  1. // 状压DP
  2. // 从人的角度
  3. class Solution {
  4. public int distributeCookies(int[] c, int k) {
  5. int n = c.length;
  6. int[] s = new int[1 << n];
  7. // for (int i = 0; i < 1 << n; i++) {
  8. // int sum = 0;
  9. // for (int j = 0; j < n; j++)
  10. // if ((i >> j & 1) == 1)
  11. // sum += c[j];
  12. // s[i] = sum;
  13. // }
  14. // 另一种预处理方法
  15. for (int i = 0; i < n; i++) {
  16. for (int u = 1 << i, j = 0; j < u; j++)
  17. s[u | j] = s[j] + c[i];
  18. }
  19. // 另一种预处理方法,更快
  20. for (int i = 0; i < 1 << n; i++) {
  21. for (int j = 0; j < n; j++) {
  22. if ((i >> j & 1) == 1) {
  23. s[i] = s[i ^ (1 << j)] + c[j];
  24. break;
  25. }
  26. }
  27. }
  28. int[] f = new int[1 << n];
  29. Arrays.fill(f, Integer.MAX_VALUE);
  30. f[0] = 0;
  31. for (int i = 1; i <= k; i++) {
  32. for (int st = (1 << n) - 1; st > 0; st--) {
  33. for (int j = st; j > 0; j = st & (j - 1))
  34. f[st] = Math.min(f[st], Math.max(f[st ^ j], s[j]));
  35. }
  36. }
  37. return f[(1 << n) - 1];
  38. }
  39. }
  1. // 二分 + 状态压缩
  2. class Solution {
  3. public int distributeCookies(int[] c, int k) {
  4. int n = c.length;
  5. int[] s = new int[1 << n];
  6. for (int i = 0; i < 1 << n; i++) {
  7. for (int j = 0; j < n; j++)
  8. if ((i >> j & 1) != 1)
  9. continue;
  10. else {
  11. s[i] = s[i ^ (1 << j)] + c[j];
  12. break;
  13. }
  14. }
  15. int[] f = new int[1 << n];
  16. int l = Arrays.stream(c).max().getAsInt(), r = Arrays.stream(c).sum();
  17. while (l < r) {
  18. int mid = l + r >> 1;
  19. Arrays.fill(f, 0x3f3f3f3f);
  20. f[0] = 0;
  21. for (int i = 0; i < 1 << n; i++) {
  22. for (int st = i; st > 0; st = i & (st - 1)) {
  23. if (s[st] <= mid)
  24. f[i] = Math.min(f[i], f[st ^ i] + 1);
  25. }
  26. }
  27. if (f[(1 << n) - 1] > k)
  28. l = mid + 1;
  29. else
  30. r = mid;
  31. }
  32. return l;
  33. }
  34. }

6094. 公司命名

给你一个字符串数组 ideas 表示在公司命名过程中使用的名字列表。公司命名流程如下:

  1. 从 ideas 中选择 2 个 不同 名字,称为 ideaA 和 ideaB 。
  2. 交换 ideaA 和 ideaB 的首字母。
  3. 如果得到的两个新名字 不在 ideas 中,那么 ideaA ideaB(串联 ideaA 和 ideaB ,中间用一个空格分隔)是一个有效的公司名字。
  4. 否则,不是一个有效的名字。

返回 不同 且有效的公司名字的数目。

示例 1:
输入:ideas = [“coffee”,”donuts”,”time”,”toffee”] 输出:6 解释:下面列出一些有效的选择方案: - (“coffee”, “donuts”):对应的公司名字是 “doffee conuts” 。 - (“donuts”, “coffee”):对应的公司名字是 “conuts doffee” 。 - (“donuts”, “time”):对应的公司名字是 “tonuts dime” 。 - (“donuts”, “toffee”):对应的公司名字是 “tonuts doffee” 。 - (“time”, “donuts”):对应的公司名字是 “dime tonuts” 。 - (“toffee”, “donuts”):对应的公司名字是 “doffee tonuts” 。 因此,总共有 6 个不同的公司名字。 下面列出一些无效的选择方案: - (“coffee”, “time”):在原数组中存在交换后形成的名字 “toffee” 。 - (“time”, “toffee”):在原数组中存在交换后形成的两个名字。 - (“coffee”, “toffee”):在原数组中存在交换后形成的两个名字。
示例 2:
输入:ideas = [“lack”,”back”] 输出:0 解释:不存在有效的选择方案。因此,返回 0 。

提示:

  • 2 <= ideas.length <= 5 * 104
  • 1 <= ideas[i].length <= 10
  • ideas[i] 由小写英文字母组成
  • ideas 中的所有字符串 互不相同

思路:
考虑每对字符相互替换的方案数
时间复杂度:O(∣Σ∣2_n_)

  1. class Solution {
  2. public long distinctNames(String[] ideas) {
  3. Map<String, Integer> map = new HashMap<>();
  4. for (String s : ideas) {
  5. String t = s.substring(1, s.length());
  6. int idx = s.charAt(0) - 'a';
  7. map.putIfAbsent(t, 0);
  8. int x = map.get(t);
  9. x |= 1 << idx;
  10. map.put(t, x);
  11. }
  12. int[][] c = new int[26][26];
  13. for (String s : map.keySet()) {
  14. int x = map.get(s);
  15. for (int i = 0; i < 26; i++) {
  16. if ((x >> i & 1) != 1) continue;
  17. for (int j = 0; j < 26; j++) {
  18. if (i == j) continue;
  19. if ((x >> j & 1) == 0)
  20. c[i][j]++;
  21. }
  22. }
  23. }
  24. long res = 0;
  25. for (int i = 0; i < 26; i++)
  26. for (int j = 0; j < 26; j++)
  27. res += 1L * c[i][j] * c[j][i];
  28. return res;
  29. }
  30. }