洛谷 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]);
}
View Code

 

posted @ 2018-04-22 14:59  qwerity  阅读(139)  评论(0编辑  收藏  举报