洛谷 P1280 尼克的任务 ( 线性DP )
题意 : 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。尼克的一个工作日为N分钟,从第一分钟开始到第N分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第P分钟开始,持续时间为T分钟,则该任务将在第P+T-1分钟结束。写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。
分析 :
根据题意,不妨试试定义 dp[i] = 1~i 这个时间段尼克最大空闲时间
但是会发现一个问题,如果是这样定义的话,那么如果当前点 i 是
有任务的话那么,肯定是从 dp[i + T] 这个状态转移到 dp[i] 的。
具体的含义就是,选择做 i 这个工作,显然在 i 这个工作的工作时间
段内是没有空闲时间的,那么 i 点的空闲时间就要从做完 i 之后的
时间点中选择最优的进行转移。所以嘚从后往前 DP ,当然这个是
取决于 DP 状态的定义,洛谷题解上有从前向后的 DP,可以去看一下
那么改变一下定义有 dp[i] = i~N 这个时间段尼克的最大空闲时间
那么对于每一个点,有两种情况
① i 这个点是某一任务的开头 dp[i] = max( dp[i], dp[i+T] )
② i 这个点没有任务 dp[i] = dp[i+1] + 1、表示当前空闲时间相比上一个点 + 1
那么这样子,对于每一个有任务的点,往后跳 T 个时间点的状态转移
就能做到如果选择了这个任务,那么将失去多少时间,得到多少时间的空闲
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10; struct ASSIGN{ int st, en, len; bool operator < (const ASSIGN &rhs)const{ return this->st > rhs.st; }; }arr[maxn]; int dp[maxn]; bool flag[maxn]; int N, K; int main(void) { memset(dp, 0, sizeof(dp)); memset(flag, false, sizeof(flag)); scanf("%d %d", &N, &K); for(int i=0; i<K; i++) scanf("%d %d", &arr[i].st, &arr[i].len), arr[i].en = arr[i].st + arr[i].len - 1, flag[arr[i].st] = true; sort(arr, arr+K); int cur = 0; for(int i=N; i>=1; i--){ if(!flag[i]) dp[i] = dp[i+1] + 1; else{ while(arr[cur].st == i){ dp[i] = max(dp[i], dp[arr[cur].en+1]); cur++; } } } return !printf("%d\n", dp[1]); }