【Tai_mount】 算法学习 - 线性动态规划 - luoguP1280 尼克的任务
想了一个下午加半个早上,但做出来感觉十分舒爽!
本篇题解作者脑抽把分钟都写成天了,没啥大问题,就不改了。
时间复杂度:O(N+K)
dp[i]表示:若该天空闲(可以接新任务),该天之前的最大空闲时间。若该天不可能空闲,则为-1
dp[i]初值均为-1(i>1),dp[1]初值为0:第一天必然空闲。(以下说的所有“空闲”都是指该天能接新任务)
设task[i][j]为:以第i天作为开始时间的第j个任务。我的程序里用数组模拟链表储存,剩下来很多空间。(所以我的代码里没有这个数组,这里为了方便表示才写的)。同时,len[i]表示第i天有len[i]个任务开始。
我们模拟的都是该天空闲的情况,若任何情况下该天都不可能空闲,dp[i]就是-1,这个点后面还会讲到。
- 若第i天空闲:
- 若第i天有新任务可以接:遍历各任务,任务j:dp[i+t[j]]=max(dp[i+t[j]],dp[i])
- 若第i天没有新任务可以接:dp[i+1]=max(dp[i]+1,dp[i+1])
所求:dp[n+1]
思路说明
(真的遇到过很多问题啊)
首先我们第一个想到的就是拿子问题设dp数组。每一个状态由两个量觉得:空闲/不空闲,第几天。值设为子问题答案。
所以设dp[i][j]为第i天前的最大空闲天数,j=0则当天空闲,j=1则当天不空闲。
这里为什么要设是第i天前呢?我们的递推是由在某个状态下,主动更新后续的状态,而非在此状态下根据前面的信息更新自己。该状态如果空闲,还需要决策接什么任务或者没任务可接,这种情况下如果设成当天结束后的空闲时间,该状态还要考虑自己值会变化的问题,整个思路就有些乱了,不如干干净净地。这样该状态下如何决策都不会影响该状态的值。
我们发现:
- 若第i天j=0:
- 若无任务可以接:dp[i+1][0]=max(dp[i][0]+1,dp[i+1][0])。也就是第i天摸鱼了,空闲时间+1
- 若有任务可以接,接了任务j,任务的持续时间为t[j]:dp[i+t[j]]=max(dp[i+t[j]],dp[i])。
- 若第i天j=1:他能做什么呢?他什么也做不了,没法接任务的状态更新不了任何东西
审视完以后,我们发现不空闲的状态不能接任务,也并非最终答案(最终答案应该是dp[n+1][0]),所以,我要他有何用?
我们简化dp数组为:第i天空闲状况下,第i天前的最长休息时间。
然后作者就这样写了,一直没过,我们漏掉一个很重要的东西:第i天一定是在空闲状况下。
如果这一天根本就不可能空闲下来,比如第一天有两个任务,13,14,那么2,3两天一定空不下来。
我们还神奇的发现,如果第i天空闲,接了一个长度为t的任务,任务持续于i~i+t-1天的。也就是说i+t这一天一定空闲。同样的,第i天空闲又没有任务可接,i+1天也同样是空闲的。
抽象出来,我们可以建一个perm的bool数组,仅当perm为true的时候才可以由此状态更新后面的状态,perm为false就直接跳过。而某状态可以把它所更新的状态的perm都设成true。
事实上后来还能简化掉perm,因为设perm和更新dp都是去操作同一个状态,如果第i天没接任务就都是设i+1,接了任务就是设i+t。那我们直接删掉perm,把dp数组初值都赋成-1,然后dp[1]单独赋成0。第一天肯定是能接任务的。
代码实现:
#include<iostream>
#include<cstring>
using namespace std;
const int N=10007;
int n,k;
int edges[N],fst[N],nxt[N],dp[N];
void input(){//用链表储存任务,接在任务的开始时间处。edges[N]里储存持续时间,因为作者是学储存图的时候学的链表,所以习惯这样写
cin>>n>>k;
int p,t;
for(int i=1;i<=k;i++){
cin>>p>>t;
edges[i]=t;
nxt[i]=fst[p];
fst[p]=i;
}
}
void dpfun(){
memset(dp,-1,sizeof(dp));
dp[1]=0;
for(int i=1;i<=n;i++){
if(dp[i]==-1) continue;
int f=fst[i];
if(!f){
dp[i+1]=max(dp[i]+1,dp[i+1]);
continue;
}
dp[i+edges[f]]=max(dp[i+edges[f]],dp[i]);
while(nxt[f]){
f=nxt[f];
dp[i+edges[f]]=max(dp[i+edges[f]],dp[i]);
}
}
}
void output(){
cout<<dp[n+1];
}
int main(){
input();
dpfun();
output();
return 0;
}
链表没有学过可以补一下哦~这里就不赘述了。
谢谢观看。

浙公网安备 33010602011771号