多状态DP
前置知识
dp的四步法(水字数):
- 确定状态
- 确定答案
- 确定状态转移方程
- 确定初始状态和边界
P1644 跳马问题
一共就上面的四个方向,并且并没有单调性,所以for
循环时先设行,再设列。
知识点:
如果dp中并没有单调性,进行状态转移方程的
for
循环要先跑列,再跑行。
-
确定状态:
dp[i][j]
直接代表 \(0,0\) 可以直接到达 \(i,j\) 的方案数。 -
确定答案:
dp[n][m]
。 -
确定状态转移方程:
dp[i][j] = dp[i+2][j-1] + dp[i+1][j-2] + dp[i - 1][j -2] + dp[i -2][j- 1]
-
确定初始状态和边界:
dp[0][0]=1 //0,0开始的方案数是1 dp[i][j]=0;//不存在方案是0
D1320 【例3.4】昆虫繁殖
这道题的思路就比上一道题的模拟dfs的过程完全不相同,因为这里要搞两个值,并且是相互依赖的。
两种形态:幼虫和成虫。
依赖关系:幼虫可以直接转变为成虫,成虫可以继续繁殖有幼虫。
随着月份的增加,整个过程一直在递增,后面的状态不会影响前面的状态
温馨提示:
dp
真正有效果的时候,是问题中包含重叠子问题
-
确定状态:
baby[i]
表示在第 \(i\) 个月幼虫的数量,dp[i]
表示在第 \(i\) 个月成虫的数量。 -
确定答案:
dp[z]
。 -
确定状态转移方程。
baby[i] = dp[i - x] * y; // 得到新生的幼虫数量 每队幼虫只使用一次,保证答案唯一 dp[i] = dp[i - 1] + baby[i - 2]; // 得到成虫的数量
-
确定初始值和边界:
dp[1-x] = 1; baby[0] = 0; // 初始条件
P7158 「dWoi R1」Password of Shady
这道题有两种写法:
大暴力(30pts)
直接求出范围,然后for
循环一个一个枚举和判断,直接求出答案。
dp(100pts)
实际上这道题给出的 \(k\) 没有用,因为 \(k\) 的值不会为0,所以所有值最后求出来的结果都是一样的。
这道题的题目隐藏了偶数的“反义词”——奇数,在求值中奇数和偶数是互补的
-
确定状态
dp[0][i] //表示有i位数字有偶数个k的总情况数量 dp[1][i] //表示有i位数字有奇数个k的总情况数量 或者 dp1[i] //表示有i位数字有偶数个k的总情况数量 dp2[i] //表示有i位数字有奇数个k的总情况数量
-
确定答案:
dp1[i]
或dp[0][i]
,输出偶数的答案 -
确定状态转移方程
dp[0][i] = dp[0][i - 1] * 9 + dp[1][i - 1] dp[1][i] = dp[1][i - 1] * 9 + dp[0][i - 1]
-
确定初始值和边界情况
dp[0][1] = 1; dp[1][1] = 8;// 因为不存在前导0
P8395 [CCC2022 S1] Good Fours and Good Fives
这个就是数字拆分的模板,只有两个数字,4和5。
这道题还有一个加强版,P1806 跑步,这个题就是数字设定范围,问整个范围内的方案数。
本题不能直接dp[i] = dp[i - 4] + dp[i - 5]
,会有重复,如下图:
-
确定状态
按照从小到大的顺序构建
dp1[i]//表示以4结尾的方案数 dp2[i]//表示以5结尾的方案数
-
确定答案:
dp1[n]+dp2[n]
-
确定状态转移方程
dp1[i] = dp1[i - 4]; dp2[i] = dpl[i - 4] + dp2[i - 5];
-
确定初始值和边界情况
dp1[4]= 1; dp2[5] = 1;