【OD机试题笔记】编码能力提升计划

题目描述

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

在小王刷题计划中,小王需要用tme[i]的时间完成编号 i 的题目。

此外,小王还可以查看答案,可以省去该题的做题时间。为了真正达到刷题效果,小王每天最多直接看一次答案。

我们定义m天中做题时间最多的一天耗时为T(直接看答案的题目不计入做题总时间)。

请你帮小王求出最小的T是多少。

输入描述
第一行输入为time,time[i]的时间完成编号 i 的题目

第二行输入为m,m表示几天内完成所有题目,1 ≤ m ≤ 180

输出描述
最小耗时整数T

** 示例1
输入
999,999,999
4

输出
0

说明>
在前三天中,小王每天都直接看答案,这样他可以在三天内完成所有的题目并不花任何时间

** 示例2
输入
1,2,2,3,5,4,6,7,8
5

输出
4

说明
第一天完成前3题,第3题看答案;
第二天完成第4题和第5题,第5题看答案;
第三天完成第6和第7题,第7提看答案;
第四天完成第8题,直接看答案:
第五天完成第9题,直接看答案

思路

1. 问题转化

目标是找到最小的单日最大耗时T,使得能将n道题按顺序分成m段,每段可跳过1道耗时最大的题,剩余总耗时≤T。

2. 二分查找(确定最小T)
  • 边界:左边界为0(极端情况每天跳过所有题),右边界为所有题目耗时总和(极端情况不跳过任何题);
  • 逻辑:每次取中点mid作为候选T,验证是否可行;可行则尝试更小T(缩右边界),不可行则增大T(扩左边界),最终找到最小可行T。
3. 贪心验证(判断T是否可行)
  • 遍历题目,维护当前段的「总耗时sum」和「段内最大耗时max_t」;
  • sum - max_t > T(去掉段内最大耗时题后仍超T),则开启新段,重置sum和max_t为当前题耗时;
  • 最终判断所需天数≤m即可(提前终止:天数超m直接返回不可行)。
4. 边界优化

若题目数≤天数(n≤m),每天做1题并跳过,直接返回T=0。

时空复杂度

  • 时间:O(n log S)(n为题目数,S为总耗时,log S≤30);
  • 空间:O(1)(仅常量空间)。

代码

function solution() {
    // 读取输入
    const time = readline().split(',').map(Number);
    const m = Number(readline());
    const n = time.length;

    // 边界情况:题目数≤天数,每天做1题且跳过,总耗时0
    if (n <= m) {
        console.log(0);
        return;
    }

    // 二分查找边界:左=0,右=所有题目总耗时
    let left = 0;
    let right = time.reduce((a, b) => a + b, 0);
    let answer = right; // 初始化为最大值

    // 核心:贪心验证函数(判断T是否可行)
    const canFinish = (T) => {
        let curDay = 1; // 已用天数(至少1天)
        let sum = 0;    // 当前段总耗时
        let max_t = 0;  // 当前段最大耗时(用于跳过)
        for (const cost of time) {
            sum += cost;
            max_t = Math.max(max_t, cost);
            // 关键:去掉当前段最大耗时后仍超T → 必须开新段
            if (sum - max_t > T) {
                curDay++;
                sum = cost;   // 新段从当前题开始
                max_t = cost; // 新段最大耗时初始化
                // 提前终止:天数超m直接返回不可行
                if (curDay > m) return false;
            }
        }
        return curDay <= m;
    };

    // 二分查找最小T
    while (left <= right) {
        const mid = left + Math.floor((right - left) / 2);
        if (canFinish(mid)) {
            answer = mid; // 记录可行的T
            right = mid - 1; // 尝试更小的T
        } else {
            left = mid + 1; // T太小,需增大
        }
    }

    console.log(answer);
}

// 测试用例(覆盖常规场景+反例)
const cases = [
    // 示例1
    `999,999,999
4`,
    // 示例2
    `1,2,2,3,5,4,6,7,8
5`,
    // 反例:[5,1,1,1,1] m=2 → 输出2
    `5,1,1,1,1
2`
];

// 模拟readline函数
let caseIndex = 0, lineIndex = 0;
const readline = (() => {
    let lines = [];
    return () => {
        if (!lineIndex) lines = cases[caseIndex].trim().split('\n').map(l => l.trim());
        return lines[lineIndex++];
    };
})();

// 执行测试
cases.forEach((_, i) => {
    caseIndex = i;
    lineIndex = 0;
    solution();
    console.log('-------');
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值