考虑今天是第i天,我必须上班,但是手头又没有任何有效期内的车票了。这个时候我必须买票。假设dp[i]表示我迄今为止已经花费的钱(不包括第i天本身),那么现在我有三种选择:
- 买一日票,那么影响的是第i+1天的状态。假设第i+1天我们需要上班,那么dp[i+1]就有一种解是dp[i]+cost[0];
- 买七日票,那么影响的是第i+7天的状态。假设第i+7天我们需要上班,那么dp[i+7]就有一种解是dp[i]+cost[1];
- 买三十日票,那么影响的是第i+30天的状态。假设第i+30天我们需要上班,那么dp[i+30]就有一种解是dp[i]+cost[2];
所以说,如果我们已知当前状态dp[i],那么dp[i+1],dp[i+7],dp[i+30]都有机会更新自己,并可能取到更小的值。那么有人会问,对于处于有效期中间的dp[j],我们是否需要更新呢?答案是否。因为那些日子我们并不需要买票。所以,我们需要再次确认我们对于dp数组状态的定义:dp[i]表示第i天,我需要买票,但是手头又没有任何有效期内的票,那么此时我已经花费了多少钱。也就是说,如果第i天我们已经有了有效车票,那么dp[i]就是没有意义的。
于是,问题来了,假设我最后一天(第365天)需要上班,但是手头已经有了一张有效车票。你说dp[365]没有意义,那我怎么结算这一年的车票费用呢?答案是,继续往后追溯,直到找这张车票失效的日期,假设是第370天,那么dp[370]就是你全年的费用(至少是一个解)。
所以dp[i]的关键就是:一定是当前没有任何有效车票。对于任何已经有有效车票的日子,dp没有意义,我们可以往后看,看车票失效的日期,就是我们结算费用的日子。
有了上述的dp定义,我们可以发现最开始的状态转移方程其实是有点问题的。比如说买了七日票,但是第i+7天我们可能并不需要上班啊。所以这张车票其实可以管到你下一个需要上班的早晨,在那个时候进行费用结算,可以变相最大化你的车票有效期。因此遇到这种情况,我们不更新dp[i+7],然是从第i+7天开始找下一个需要上班的日子,我们更新这个dp[i+7+gapPeriod]。
最后,我们如何从dp里找最终的答案呢?根据上面的定义,车票真正的“实际失效期”一定是在365天之后!(因为最后一张车票一定管到你全年结束,包括你年末不上班) 所以我们在dp[366]到dp[365+30]这些日子找最小值即可。