P1280 尼克的任务——又是一道经典的dp绿题,值得一看

尼克的任务

题目描述

尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。

尼克的一个工作日为 n n n 分钟,从第 1 1 1 分钟开始到第 n n n 分钟结束。当尼克到达单位后他就开始干活,公司一共有 k k k 个任务需要完成。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第 p p p 分钟开始,持续时间为 t t t 分钟,则该任务将在第 ( p + t − 1 ) (p+t-1) (p+t1) 分钟结束。

写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。

输入格式

输入数据第一行含两个用空格隔开的整数 n n n k k k

接下来共有 k k k 行,每一行有两个用空格隔开的整数 p p p t t t,表示该任务从第 p p p 分钟开始,持续时间为 t t t 分钟。

输出格式

输出文件仅一行,包含一个整数,表示尼克可能获得的最大空暇时间。

样例 #1

样例输入 #1

15 6
1 2
1 6
4 11
8 5
8 1
11 5

样例输出 #1

4

提示

数据规模与约定
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 4 , 1 ≤ k ≤ 1 0 4 , 1 ≤ p ≤ n , 1 ≤ p + t − 1 ≤ n 1 \leq n \leq 10^4,1 \leq k \leq 10^4,1 \leq p \leq n,1 \leq p+t-1 \leq n 1n104,1k104,1pn,1p+t1n

分析

这道题我们第一反应就是从前往后进行分析,这是我们会列出一下的不完整的状态转移方程
d p [ i ] = d p [ i − 1 ] + 1 dp[i]=dp[i-1]+1 dp[i]=dp[i1]+1
d p [ i ] = d p [ i − t ] ( 此处的 t 为当前任务的已经执行了的时间 ) dp[i]=dp[i-t](此处的t为当前任务的已经执行了的时间) dp[i]=dp[it](此处的t为当前任务的已经执行了的时间)
那么这个时候问题来了,你怎么知道 t t t呢?有人会说标记一下开始的位置,用当前的位置减去开始的位置就行了啊?但是你怎么确定当前的这个任务你选不选呢?

所以这里我们是不可以从前往后分析的,那么我们就换一种想法,从后往前来遍历,那么此时的状态转移方程就变成了
d p [ i ] = d p [ i + 1 ] + 1 / / 如果当前没任务 dp[i]=dp[i+1]+1//如果当前没任务 dp[i]=dp[i+1]+1//如果当前没任务
d p [ i ] = m a x ( d p [ i ] , d p [ i + a [ j ] . e d + 1 ] ) / / 如果有任务 dp[i]=max(dp[i],dp[i+a[j].ed+1])//如果有任务 dp[i]=max(dp[i],dp[i+a[j].ed+1])//如果有任务

算法步骤

  • n读取输入数据,包括一天的总分钟数 n n n 和任务数量 k k k,以及每个任务的开始时刻和持续时间。
  • 初始化 d p dp dp 数组,并根据任务的安排情况标记 u s e d used used 数组,用于记录任务是否被安排。
  • 使用动态规划算法,从最后一分钟开始向前遍历,更新 d p dp dp 数组的值。
  • 如果当前分钟没有任务安排,则可以选择休息, d p [ i ] = d p [ i + 1 ] + 1 dp[i] = dp[i+1] + 1 dp[i]=dp[i+1]+1
  • 如果当前分钟有任务安排,则需要检查这些任务,并选择可以获得最大空闲时间的任务。
  • 输出 d p [ 1 ] dp[1] dp[1],即表示尼克可能获得的最大空闲时间。
#include<bits/stdc++.h>
using namespace std;

// 定义结构体存储任务的起始时间和结束时间
struct Task {
    int start;
    int end;
} a[20010];

int used[20010], dp[200100];

int main() {
    int n, k;
    cin >> n >> k; // 读取总分钟数和任务数量

    // 读取每个任务的起始时间和持续时间,并将结束时间标准化
    for (int i = 1; i <= k; i++) {
        cin >> a[i].start >> a[i].end;
        a[i].end = a[i].start + a[i].end - 1;
        used[a[i].start] = 1; // 标记任务开始时间,表示该时间点有任务安排
    }

    // 动态规划过程
    for (int i = n; i >= 1; i--) {
        if (used[i] == 0) // 如果当前时刻没有任务安排
            dp[i] = dp[i + 1] + 1; // 可以选择休息,空闲时间加一
        else {
            // 如果当前时刻有任务安排
            for (int j = 1; j <= k; j++) {
                if (a[j].start == i) { // 如果有任务在当前时刻开始
                    dp[i] = max(dp[i], dp[a[j].end + 1]); // 更新当前时刻的最大空闲时间
                }
            }
        }
    }

    cout << dp[1]; // 输出尼克可能获得的最大空闲时间
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值