LCP 12. 小张刷题计划

难度中等

为了提高自己的代码能力,小张制定了 LeetCode 刷题计划,他选中了 LeetCode 题库中的 n 道题,编号从 0 到 n-1,并计划在 m 天内按照题目编号顺序刷完所有的题目(注意,小张不能用多天完成同一题)。

在小张刷题计划中,小张需要用 time[i] 的时间完成编号 i 的题目。此外,小张还可以使用场外求助功能,通过询问他的好朋友小杨题目的解法,可以省去该题的做题时间。为了防止“小张刷题计划”变成“小杨刷题计划”,小张每天最多使用一次求助。

我们定义 m 天中做题时间最多的一天耗时为 T(小杨完成的题目不计入做题总时间)。请你帮小张求出最小的 T是多少。

示例 1:

输入:time = [1,2,3,3], m = 2

输出:3

解释:第一天小张完成前三题,其中第三题找小杨帮忙;第二天完成第四题,并且找小杨帮忙。这样做题时间最多的一天花费了 3 的时间,并且这个值是最小的。

示例 2:

输入:time = [999,999,999], m = 4

输出:0

解释:在前三天中,小张每天求助小杨一次,这样他可以在三天内完成所有的题目并不花任何时间。

 

限制:

  • 1 <= time.length <= 10^5
  • 1 <= time[i] <= 10000
  • 1 <= m <= 1000
题意概述
给定一个数组,将其划分成 MM 份,使得每份元素之和最大值最小,每份可以任意减去其中一个元素。

题解
如果不考虑每份可以任意减去一个元素,就是一个经典的二分问题,具有单调最优的性质:如果最大值为 tt 可以满足条件划分,那么最大值为 t+1t+1 也可以。所以就直接二分最大值 tt,找到最小满足条件的 tt 即可。

本题加了一个条件:每份可以删除任意一个数组。为了能够让最大值最小,显然每份中删去的一定是最大元素,所以在二分判定可行性时,维护当前序列的最大值,然后记录删除最大值的结果,也就是说二分的判定是:是否可以让每组删除最大值之后,总和都小于等于 tt。


class Solution {
public:
    bool Check(int limit, vector<int> cost, int day) {
      // 每组划分 limit 的最大和,贪心划分看有多少组
        int useday, totaltime, maxtime;
        useday = 1; totaltime = maxtime = 0;
        for (int i=0; i<cost.size(); ++i) {
            int nexttime = min(maxtime, cost[i]);
            if (nexttime + totaltime <= limit) {
                totaltime += nexttime;
                maxtime = max(maxtime, cost[i]);
            } else {
                ++useday;
                totaltime = 0;
                maxtime = cost[i];
            }
        }
        return (useday <= day);
    }
    int minTime(vector<int>& time, int m) {
        int left, right, middle;
        left = right = 0;
        for (int i=0; i<time.size(); ++i) {
            right += time[i];
        }
        while (left <= right) {
            middle = (left + right) >> 1;
            if (Check(middle, time, m)) right = middle - 1;
            else left = middle + 1;
        }
        return left;
    }
};

复杂度分析 时间复杂度:O(N\log M)O(NlogM),其中 NN 是数组的大小,MM 是数组所有元素之和。 空间复杂度:O(N)O(N)。

如果题目改一下,小张不可以使用场外求助功能,怎么解答呢?

解题如下: 


public int minTime(int[] time, int m) { if (time.length <= m) { return 0; } int left, right,mid; left = right = 0; for (int i = 0; i < time.length; i++) { right += time[i]; } while (left <= right) { mid = (left + right) >> 1; if (check2(mid, time, m)) { right = mid -1; } else { left = mid +1; } } return left; } private boolean check2(int limit, int[] time, int day) { int useday = 1, totalTime =0 ; for (int i = 0; i < time.length; i++) { int nextTime = time[i]; if (nextTime + totalTime <= limit) { totalTime += nextTime; } else { ++useday; totalTime = nextTime; } } return (useday <= day); }

 

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/xiao-zhang-shua-ti-ji-hua/solution/xiao-zhang-shua-ti-ji-hua-er-fen-cha-zhao-by-leetc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

1439. 有序矩阵中的第 k 个最小数组和

难度困难

给你一个 m * n 的矩阵 mat,以及一个整数 k ,矩阵中的每一行都以非递减的顺序排列。

你可以从每一行中选出 1 个元素形成一个数组。返回所有可能数组中的第 k 个 最小 数组和。

 

示例 1:

输入:mat = [[1,3,11],[2,4,6]], k = 5
输出:7
解释:从每一行中选出一个元素,前 k 个和最小的数组分别是:
[1,2], [1,4], [3,2], [3,4], [1,6]。其中第 5 个的和是 7 。  

示例 2:

输入:mat = [[1,3,11],[2,4,6]], k = 9
输出:17

示例 3:

输入:mat = [[1,10,10],[1,4,5],[2,3,6]], k = 7
输出:9
解释:从每一行中选出一个元素,前 k 个和最小的数组分别是:
[1,1,2], [1,1,3], [1,4,2], [1,4,3], [1,1,6], [1,5,2], [1,5,3]。其中第 7 个的和是 9 。 

示例 4:

输入:mat = [[1,1,10],[2,2,9]], k = 7
输出:12

 

提示:

  • m == mat.length
  • n == mat.length[i]
  • 1 <= m, n <= 40
  • 1 <= k <= min(200, n ^ m)
  • 1 <= mat[i][j] <= 5000
  • mat[i] 是一个非递减数组
通过次数5,521
 
提交次数10,111
 
  public int kthSmallest(int[][] mat, int k) {
        int m = mat.length;
        int n = mat[0].length;
        int left = 0, right = 0;
        for (int i = 0; i < m; i++) {
            left += mat[i][0];
            right += mat[i][n - 1];
        }
        int initSum = left;
        while (left < right) {
            int mid = left + (right - left) / 2;
            int count = count(mat, mid, initSum, 0, k);
            if (count >= k) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    private int count(int[][] mat, int mid, int lastSum, int rowIndex, int k) {
        int m = mat.length;
        int n = mat[0].length;
        if (rowIndex == m) {
            return 1;
        }
        int count = 0;

        for (int col = 0; col < n; col++) {
            int nextSum = lastSum - mat[rowIndex][0] + mat[rowIndex][col];
            if (nextSum <= mid) {
                count += count(mat, mid, nextSum, rowIndex + 1, k);
                if (count >= k) {
                    return count;
                }
            } else {
                    break;
            }
        }
        return count;
    }

 

 

 

posted on 2022-02-10 19:45  yuluoxingkong  阅读(44)  评论(0)    收藏  举报