Horry和John打算晚上一起看恐怖影片,Horry精神很好,但是John比较疲劳容易犯困。John初始的兴奋值为74单位,然后每过1分钟就降低1个单位,降到0以下就会入睡,一旦睡着后就无法再看后面的影片。但是,当John被特定的恐怖场景刺激时,兴奋值会因此而升高47个单位。每部电影长短各异,且每部都含有一个特定恐怖场景。现在Horry想根据影片的长度以及其中恐怖场景出现的时间等信息,来排看片计划,使得John在入睡之前能从头至尾看全的影片的数目达到最大。要求返回这样一个看片计划表(影片序号数组),如果存在多个,则返回其中按字典序排在最前的那个。
示例:影片长度length={100, 100, 100, 100},恐怖时刻scary={77, 76, 75, 74},输出Returns: {3, 0, 1, 2 },这种排片计划下John才能看完3号电影,其他顺序都不是最佳的。
情景分析:当John在看第i片影片时,处在某个兴奋值,如果兴奋值在恐怖场景出现之前降至0,则John就将入睡,无法观看后续影片,如果兴奋值随着恐怖场景的出现得到补充,John就能坚持的久些。如果某个看片计划能使得John的兴奋值在不同的时刻恰到好处地得到补充,则能帮助John看更多的影片。
问题求解需要同时考虑看片最多且按字典序排列最优。第一个最优,往往倾向于状态dp,每个状态由选择状态表(一组比特位)以及该选择下的最后一部影片索引表示,配合pre数组记录某状态下的最近一部电影,就可以从后往前追溯生成选择路径。
第i次: scare[status][i] (status为选择状态,i为最后一部看完的电影)
第i+1次:若第j部影片不在status选择中,且John按规则能够看完,则可以前进至新状态scare[status|(1<<j)][j],且记录pre[status|(1<<j)][j] = i
从某个最佳状态j追溯时时,设pre[status][j]=i,得到上个状态status'=status^(1<<j),然后再从pre[status'][i]=k,继续追溯k及其更前的状态……从而得到路径,比较并选择其中字典序最优的一个返回。
这个想法实现起来比较直观,能够通过大部分测试用例,但是这种从后往前的设计,当可能的选择很多时,路径的比较选择以及中间信息开销会比较大。
所以,实际测试下来,还是要尝试路径上从前往后求解的方式。当打算看一组影片movieSet时,我们可以按索引从前往后选择能看完的电影,比如观看影片i,剩下的movieSet变成movieSet^(1<<i),scare变成scare+47-length[i],一个相同子问题,假设子问题已经解决,我们就可以得到选择i影片的解,然后依次尝试其他i‘的情况,取各个子问题的解的最大值,可以求得当前问题的最优解,解相同的情况下取以索引小者为优,从而可以保证按字典序最优。
新的思路是典型的自上而下的求解方式,所以可以考虑备忘录搜索。
搜索结束后,根据select数组从前往后遍历排片计划路径,由于搜索只限于可选解的情况,所以select里的排片计划不一定包含所有影片,对于未列的影片,意味着John已经无法观看,直接将剩余影片按字典序输出即可。
问题还可以变成求John最长的看片时间等等,思路类似。