四年前,一位师兄就和我说过:动态规划是当前状态的转移。
可能有些记不清了,但是,这句话印在我脑海里了。那么我们看下面这两道题目。
题目一
小M面对一组从 1 到 9 的数字,这些数字被分成多个小组,并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。
numbers: 一个由多个整数字符串组成的列表,每个字符串可以视为一个数字组。小M需要从每个数字组中选择一个数字。
例如对于[123, 456, 789],14个符合条件的数为:147 149 158 167 169 248 257 259 268 347 349 358 367 369。
要满足条件,奇数的选择必须是偶数次。
很显然,这道题应该将数组中的每个元素的各个位上的奇偶分别来讨论。于是建立两个数组,分别是odd和even,存储着,如果从第i个数中挑1个时,奇数和偶数的选择数分别是多少。
当我们有了这个选择数之后,我们就可以进行下一步选择。即从i到i+1。i就可以看作为当前的状态。而i+1是下一步状态。
所以当前选择是由上一个选择决定的。那么我们需要找出他们的关系。
我们设dp[i][0]和dp[i][1]为第i次选择后的odd选择为偶次和odd选择为奇次的情况。于是,我们有
dp[i][0] = dp[i-1][0] * even[i] + dp[i-1][1]*odd[i];
dp[i][1] = dp[i-1][0] * odd[i] + dp[i-1][1] * even[i];
题目二
小E在一个游戏中遇到了 n 个按顺序出现的怪物,每个怪物都有其特定的血量 hi 和攻击力 ai。小E的初始血量为 H,攻击力为 A。 游戏规则如下:
- 小E可以击败一个血量和攻击力都小于她当前属性的怪物。
- 对于第一个击败的怪物,需要满足其血量小于 H 且攻击力小于 A。
3. 击败怪物后,小E会获得该怪物的属性值。(一个混淆视听的条件) - 为了保持战斗节奏,要求击败的怪物序列中,后一个怪物的血量和攻击力都必须严格大于前一个怪物。
小E想知道,她最多能击败多少怪物。
状态转移,状态转移!
我们关注,当小E击败第i个怪物时,他已经击败的最多的怪物。记为dp[i]。我们要求的其实也就是,dp[n]。那如何进行状态转移呢?此时就不只是上一步转移了,而是,要看过去所有状态,这是因为它是有条件的更新。即需要保证后一个怪物的血量和攻击力都必须严格大于前一个怪物。所以dp[i]取值不仅取决于过去的dp,还取决于MONSTOR[i]值。
if(小E血量攻击小于怪i){
continue;
}
dp[i]=1;
for(int j=0;j<i;j++){
if(后一个怪保持血量攻击力严格大于)
dp[i] = max(dp[j]+1,dp[i]);
}
总结
动态规划还是比较费脑子的。需要利用一些通勤时间,多设想,多推演。