Skip to content

Files

Latest commit

7cc03d3 · Nov 15, 2024

History

History
262 lines (214 loc) · 6.98 KB

File metadata and controls

262 lines (214 loc) · 6.98 KB
comments edit_url
true

题目描述

为了提高自己的代码能力,小张制定了 LeetCode 刷题计划,他选中了 LeetCode 题库中的 n 道题,编号从 0n-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

解法

方法一:贪心 + 二分查找

我们可以将题意转换为,将题目最多分成 m 组,每一组去掉最大值后不超过 T ,求最小的满足条件的 T

我们定义二分查找的左边界 l e f t = 0 ,右边界 r i g h t = i = 0 n 1 t i m e [ i ] ,二分查找的目标值为 T

我们定义函数 c h e c k ( T ) ,表示是否存在一种分组方案,使得每一组去掉最大值后不超过 T ,并且分组数不超过 m

我们可以用贪心的方法来判断是否存在这样的分组方案。我们从左到右遍历题目,将题目耗时加入当前总耗时 s ,并更新当前分组的最大值 m x 。如果当前总耗时 s 减去当前分组的最大值 m x 大于 T ,则将当前题目作为新的分组的第一题,更新 s m x 。继续遍历题目,直到遍历完所有题目。如果分组数不超过 m ,则说明存在这样的分组方案,返回 t r u e ,否则返回 f a l s e

时间复杂度 O ( n × log S ) ,空间复杂度 O ( 1 ) 。其中 n S 分别为题目数量和题目总耗时。

Python3

class Solution:
    def minTime(self, time: List[int], m: int) -> int:
        def check(t):
            s = mx = 0
            d = 1
            for x in time:
                s += x
                mx = max(mx, x)
                if s - mx > t:
                    d += 1
                    s = mx = x
            return d <= m

        return bisect_left(range(sum(time)), True, key=check)

Java

class Solution {
    public int minTime(int[] time, int m) {
        int left = 0, right = 0;
        for (int x : time) {
            right += x;
        }
        while (left < right) {
            int mid = (left + right) >> 1;
            if (check(mid, time, m)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    private boolean check(int t, int[] time, int m) {
        int s = 0, mx = 0;
        int d = 1;
        for (int x : time) {
            s += x;
            mx = Math.max(mx, x);
            if (s - mx > t) {
                s = x;
                mx = x;
                ++d;
            }
        }
        return d <= m;
    }
}

C++

class Solution {
public:
    int minTime(vector<int>& time, int m) {
        int left = 0, right = 0;
        for (int x : time) {
            right += x;
        }
        auto check = [&](int t) -> bool {
            int s = 0, mx = 0;
            int d = 1;
            for (int x : time) {
                s += x;
                mx = max(mx, x);
                if (s - mx > t) {
                    s = x;
                    mx = x;
                    ++d;
                }
            }
            return d <= m;
        };
        while (left < right) {
            int mid = (left + right) >> 1;
            if (check(mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
};

Go

func minTime(time []int, m int) int {
	right := 0
	for _, x := range time {
		right += x
	}
	return sort.Search(right, func(t int) bool {
		s, mx := 0, 0
		d := 1
		for _, x := range time {
			s += x
			mx = max(mx, x)
			if s-mx > t {
				s, mx = x, x
				d++
			}
		}
		return d <= m
	})
}

TypeScript

function minTime(time: number[], m: number): number {
    let left = 0;
    let right = time.reduce((a, b) => a + b);
    const check = (t: number): boolean => {
        let s = 0;
        let mx = 0;
        let d = 1;
        for (const x of time) {
            s += x;
            mx = Math.max(mx, x);
            if (s - mx > t) {
                s = x;
                mx = x;
                d++;
            }
        }
        return d <= m;
    };
    while (left < right) {
        const mid = (left + right) >> 1;
        if (check(mid)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

Swift

class Solution {
    func minTime(_ time: [Int], _ m: Int) -> Int {
        var left = 0
        var right = time.reduce(0, +)

        while left < right {
            let mid = (left + right) / 2
            if check(mid, time, m) {
                right = mid
            } else {
                left = mid + 1
            }
        }
        return left
    }

    private func check(_ t: Int, _ time: [Int], _ m: Int) -> Bool {
        var sum = 0
        var maxTime = 0
        var days = 1

        for x in time {
            sum += x
            maxTime = max(maxTime, x)
            if sum - maxTime > t {
                sum = x
                maxTime = x
                days += 1
            }
        }
        return days <= m
    }
}