【题解】尼克的任务(DP)

探讨了尼克如何在限定工作时间内,通过合理选择任务来最大化个人空暇时间的问题。利用动态规划方法,逆序计算状态转移方程,确保任务安排的最优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

【题目描述】
尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。 尼克的一个工作日为NNN分钟,从第一分钟开始到第NNN分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去写成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第PPP分钟开始,持续时间为TTT分钟,则该任务将在第P+T−1P+T-1P+T1分钟结束。 写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。
【输入】
输入数据第一行包含两个用空格隔开的整数NNNKKK1≤N≤100001\leq N\leq100001N100001≤K≤100001\leq K\leq100001K10000NNN表示尼克的工作时间,单位为分,KKK表示任务总数。 接下来共有KKK行,每一行有两个用空格隔开的整数PPPTTT,表示该任务从第PPP分钟开始,持续时间为TTT分钟,其中1≤P≤N,1≤P+T−1≤N1\leq P\leq N,1\leq P+T-1\leq N1PN1P+T1N
【输出】
输出文件仅一行包含一个整数表示尼克可能获得的最大空暇时间。
【样例输入】

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

【样例输出】

4

算法分析

对于DPDPDP题目,一种简单的设置状态的方法就是,题目怎么问,怎么设置状态。
状态:
f[i]f[i]f[i]——第iii分钟结束后获得的最大空闲时间。
状态转移方程
(1)在第iii分钟时如果没有任务:
f[i]=f[i−1]+1f[i]=f[i-1]+1f[i]=f[i1]+1
(2)在第iii分钟有任务,选择第jjj个任务做,持续第T[j]T[j]T[j]分钟:
f[i]=max(f[i],f[i−T[j]])f[i] = max( f[i] , f[i-T[j]])f[i]=max(f[i],f[iT[j]])i≥T[j]i \geq T[j]iT[j],因为在i−T[i]+1i-T[i]+1iT[i]+1分钟,正在开始做这个任务,所以是i−T[j]i-T[j]iT[j]
看到这里,可能大家都发现问题了,题目说当前时刻有任务就必须要做,如果正在做任务过程中,则不做当前时刻的任务。如果我要做第jjj个任务,持续时间为T[j]T[j]T[j],我们不能确定在i−T[j]+1i-T[j]+1iT[j]+1分钟时,是否正在做着其他任务,如果正在做其他任务,那么此时就不能做第jjj个任务。

如何解决这个问题呢?我们可以按时间逆序讨论:
状态转移方程
(1)在第i分钟时如果没有任务:
f[i]=f[i+1]+1f[i]=f[i+1]+1f[i]=f[i+1]+1
(2)在第iii分钟有任务,选择第jjj个任务做,持续第T[j]T[j]T[j]分钟:
f[i]=max(f[i],f[i+T[j]])f[i] = max( f[i] , f[i+T[j]])f[i]=max(f[i],f[i+T[j]])
这样,我就能够保证在第iii分钟开始第jjj个任务,在第i+T[j]−1i+T[j]-1i+T[j]1分钟结束,第i+T[j]i+T[j]i+T[j]分钟又可以重新选择做哪些任务,不会有影响。

参考程序

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 100010
using namespace std;
int n,k,f[N],s[N],p[N],t[N]; 
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%d%d",&p[i],&t[i]);
        s[p[i]]++;		//统计当前时刻有多少个任务
    }   
    for(int i=n;i>=1;i--)    //逆推 
    {
        if(s[i]==0) f[i]=f[i+1]+1;      //当前无任务 
        else
        {
            for(int j=1;j<=k;j++)	
            {
                if(p[j]==i)         //当前任务在i时刻开始的,选择做哪一个任务
                    f[i]=max(f[i],f[i+t[j]]);
            }
        }
    }
    cout<<f[1]<<endl;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值