什么叫“知其所以然”?就是认为所有的理所当然都不显然。正解一抓一大把,但是怎么想到的?很少有人能解释。今天就发现尼克的任务真不简单。
一.我们直接从一个正解开始
先定义f[i]表示在第i时刻时已经积累的偷懒时间(怎么想出来的?只能一一枚举所有的可能定义并判断他们的可行性,后面我们将会如此讨论其他不同的定义)。我们可以推出方程:
①当前有任务t,a[t]表示任务持续时间,则f[i]=max(f[i+a[t]]);②当前无任务,则f[i]=f[i-1]+1。
二.问题来了,对于f[i],一个依靠i后面的f[i+a[t]],一个依靠i前面的f[i-1],怎么for循环?
先别急着否决原来的定义,思考是否可以调整其中一个方程(有无等价写法),使得两者能够统一进行正序for循环或逆序for循环。
对于第一个方程f[i]=max(f[i+a[t]]),其实等价于f[i]=max(f[i-a[t]]),只不过前一个的i表示开始任务前的时刻,后一个是结束任务后的时刻。这样不就可以两个方程同时正序for循环了吗?于是交上去WA了。
三.为何不能通过修改第一个方程来正序for循环?
如此修改,则完整转移方程应该为:
f[i]=max(f[i-a[t]]),同时f[i]=max(f[i],f[i-1]+1).
注意到与上面的区别了吗?上面的有条件语句(如果有任务则①,无任务则②),而修改后的情况没有。因为当尼克来到某个有任务结束的第i时刻,他可能是刚刚硬着头皮做任务熬过来的,也可能是在此之前早早结束了任务玩手机混过来的,但我们无法知道他到底是怎么过来的,所以我们只能把这两种情况考虑到最优解f[i]上来了。
这样的结果是,因为f[i]=f[i-a[t]]是没有偷懒时间加成的,而 f[i]=f[i-1]+1有(这不每次转移都加了1嘛),因此f[i]的最优结果必定是由f[i-1]转移过来,f[i-1]又由f[i-2]转移过来,最后f[i-...]又由f[0]转移过来,那多爽,不用工作了。可惜注意到题目有先决条件,尼克在当前时刻有工作的时候必须工作,这种转移方式完全没有顾及这一点,因此这样for循环不可行。
尼克的选择权在于任务开始时他可以自由决定做哪个任务,而选择根据是做完任务后的情况或状态,因此要赢在当下就要洞悉未来。显然最佳方案是选择f[i+a[t]]最大的,也就是继承最优子结构。因此,循环在这种思路下只能是逆向的,
(看看我刚开始写的解释,各种显然,各种名言,各种毫无逻辑。)
我们还是尝试修改第二个方程吧。f[i]=f[i-1]+1,等价于f[i]=f[i+1]-1。这个减一只是表示一种继承关系(每过一个时刻偷懒时间加一),如果我们决定逆向循环的话,这个方程完全可以写成f[i]=f[i+1]+1,与式子的变形无关,但本质是一样的(继承关系)。
综上,正确方程为:
①当前有任务t,a[t]表示任务持续时间,则f[i]=max(f[i+a[t]]);②当前无任务,则f[i]=f[i+1]+1。
这样这道题就能A了。但是我们还有一些问题……
四.能不能定义f[i]表示做了第i个任务后积累的工作时间呢?
如果这样定义,因为变量i与任务有关,我们只能直接记录任务持续的总时间。但这不是问题,我们先求出最少的工作时间,被上班总时间相减不就得到最长偷懒时间了吗?
于是我们像模像样的推出转移方程:
for i 1->n,for j 0->i-1 ,若i在做完任务j后,则f[i]=min(f[j]+a[i])
这就好笑了,如果有的选,谁会去工作呢?因此还是老问题,这个方程无法满足题目的限制条件:尼克在当前时刻有工作的时候必须要工作。而这种做法无法保证这个条件。
有问题要上,没有问题创造问题也要上。
如果尼克是个工作狂,我把题目求解改成:写一个程序计算尼克应该如何选取任务,才能获得最大的工作时间。那会怎样?
一.能不能定义f[i]表示在第i时刻时已经积累的工作时间呢?
可以。我们看推出的方程:
f[i]=f[i-1]; f[i]=max(f[i],f[i-a[t]+1]+a[t])。a[t]表示在该时刻结束的任务的时长。文字解释就是尼克可以选择回到过去(即i-a[t]+1,指该任务开始时的那个时刻)补做该任务,这样完全可以。
同时,像下面一样,倒着for循环也没问题:
f[i]=f[i+1]; f[i]=max(f[i],f[[i+a[t]-1]+a[t]);
为什么这样子正着倒着都没问题呢?请留意题目条件所求,然后比较一下方程。思考:尼克在当前时刻有工作的时候必须要工作吗?这次可以了,因为这次是多多益善,就算老板不逼迫,尼克也会在在当前时刻有工作的时候去工作,自动遵循这个限制了。
二.能不能定义f[i]表示做完第i个任务后积累的工作时间呢?
这种方法可以解锁了,原因同上。
注意不能定义成f[i]表示前i个任务后积累的工作时间,因为我们需要通过判断第i个任务和第j个任务是否冲突来限制j,因此必须要记录当前情况所做的最后一个任务是什么(是i)
方程就是,f[i]=max(f[j]+a[i]),最终结果取所有计算过的f[i]的最大值(不一定是f[n]哦)。
我这里提出的“工作狂尼克的任务”问题其实就是“挤奶的时间”这道题。
这两道题还有一种图论的方法,不过这就超纲了,再说。
原文:https://www.cnblogs.com/reshuffle/p/11478004.html