尼克的任务
题目描述
尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。
尼克的一个工作日为 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+t−1) 分钟结束。
写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。
输入格式
输入数据第一行含两个用空格隔开的整数 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 1≤n≤104,1≤k≤104,1≤p≤n,1≤p+t−1≤n。
分析
这道题我们第一反应就是从前往后进行分析,这是我们会列出一下的不完整的状态转移方程
d
p
[
i
]
=
d
p
[
i
−
1
]
+
1
dp[i]=dp[i-1]+1
dp[i]=dp[i−1]+1
d
p
[
i
]
=
d
p
[
i
−
t
]
(
此处的
t
为当前任务的已经执行了的时间
)
dp[i]=dp[i-t](此处的t为当前任务的已经执行了的时间)
dp[i]=dp[i−t](此处的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;
}