刷题系列——计数 dp

HDU 4165 Pills

一个瓶子里有 n n 片药,每天取出一片药。如果取到的药是整片的,就把它分成两半,吃掉其中的一半,另一半重新放入瓶中。如果取到半片药,则直接吃掉。问 2 × n 2 \times n 天内吃完药有多少种取法。

只有取出一个完整药片后,才会让袋子里增加一个半药片。也就是说我们只有在取出 n n 个完整药片和 n n 个半药片后,药袋才能为空。第一个做法是 dp。将问题转换成 一个 n × n n \times n 的正方形。从 ( 1 , 1 ) (1,1) 出发,如果取出了完整药片,那么就往下走一步;如果取出了半药片,那么就往右走一步,不能超出正方形的边界,求到达 ( n , n ) (n,n) 的方案数。设 f i , j f_{i,j} 为从 ( 1 , 1 ) (1,1) 走到 ( i , j ) (i,j) 的方案数。转移方程为

f i , 0 = 1 f_{i,0}=1

f i , j = f i , j 1 + f i 1 , j ( j i ) f_{i,j}=f_{i,j-1}+f_{i-1,j} \quad (j \leq i)

第二个做法是卡特兰数,将问题转换成括号匹配序列即可。

CF44H Phone Number

给你一个序列,生成一个新序列。第一个数任意选,之后的第 i i 个新数 = = i 1 i-1 个新数 + + i i 个原数的一半来生成。如果乘除,则新数 i i 唯一,否则新数 i i 可以向上或向下取整。求共能生成多少个不同的序列。

d p i , j dp{i,j} 表示确定了前 i i 位且第 i i 位为 j j 的方案数,填表不好推,不如用刷表来转移。令 x = i + 1 , j + a i + 1 2 x=\left \lfloor \frac{i+1,j+a_{i+1}}{2} \right \rfloor y = i + 1 , j + a i + 1 2 y=\left \lceil \frac{i+1,j+a_{i+1}}{2} \right \rceil

f i + 1 , x = f i + 1 , x + f i , j f_{i + 1, x} = f_{i + 1, x} + f_{i,j}

f i + 1 , y = f i + 1 , y + f i , j ( x y ) f_{i + 1, y} = f_{i + 1, y} + f_{i,j} \quad (x \not= y)

值得一提的是,如果生成的新序列与原序列相同,那么方案数少一。

CF1105C Ayoub and Lost Array

有一个长度为 n n 的序列,每个元素在 [ l , r ] [l,r] 内。求有多少不同的和为 3 3 的倍数的序列。

f i , 0 2 f_{i,0 \sim 2} 为前 i i 个数的和余数为 0 2 0 \sim 2 的方案数。令 s 0 2 s_{0 \sim 2} [ l , r ] [l,r] m o d    3 \mod 3 余数为 0 2 0 \sim 2 的数的个数,可以 O ( 1 ) O(1) 预处理。转移方程为

f i , j = s i × f 0 , j 1 + s ( i 1 ) m o d    3 × f 1 , j 1 + s ( i 2 ) m o d    3 × f 2 , j 1 f_{i,j}=s_i \times f_{0,j-1} + s{(i-1) \mod 3} \times f_{1,j-1} + s_{(i-2) \mod 3} \times f_{2,j-1}

HDU 2569 彼岸

f i f_i 为有多少个长度为 i i 的合法颜色。考虑前两个颜色,如果不同同方案数为 f i 2 × 3 f_{i-2} \times 3 ,如果相同那么只能选两个颜色,先从 f i 1 f_{i-1} 转移,因为 f i 1 f_{i-1} 包含了 f i 2 f_{i-2} 所以方案数为 ( f i 1 f i 2 ) × 2 (f_{i-1} - f_{i-2}) \times 2 。综上所述,化简后的转移方程为

f 1 = 1 , f 2 = 9 f_1=1,f_2=9

f i = 2 × f i 2 + f i 2 f_i=2 \times f_{i-2} +f_{i-2}

TOJ 1017 Staircases

给定一个数,求将 n n 个数划分为单调递增序列的和的方案数。

有二维状态的写法,不过一维状态很妙。

f i , j f_{i,j} 为用了 i i 个砖块最大高度为 j j 的方案数。对于 f i , j f_{i,j} ,可以在高度为 j 1 j-1 的一列加上一块,也可以在高度为 j 1 j-1 的后面那一列新建一个高度为 j j 的梯子。所以状态转移为

f i , j = f i , j 1 + f i j , j 1 f_{i,j} = f_{i,j-1} + f_{i-j,j-1}

可以发现, j j 只与 j 1 j-1 有关,可以把 j j 的那一维滚动优化掉,因为是滚动数组,所以转移前面就没了。所以枚举一个 j j ,在边界中枚举一个 i i 进行转移。

CF176B Word Cut

求经过 k k 次将 S S 划分成两部分并交换使 S S 串变成 T T 串的方案数。

可以发现,每次可以将原串变为本质不同的其他串,也可以从其他串变为原串。无论多少次变换,归根结底都可以通过仅一次变换求出,所以先预处理出一个 x x 来表示原串通过一次变换有多少个 T T 。暴力和 kmp 都可以,由于数据范围不大所以选择前者。

f i , 0 f_{i,0} 为经过 i i 次变换所产生的原串个数, f i , 1 f_{i,1} 为其他串的个数。不如将 T T 看作原串。如果 S T S \not= T ,那么 S S 为其他串。转移方程如下

f i , 0 = x × f i 1 , 1 + ( x 1 ) × f i 1 , 0 f_{i,0} = x \times f_{i-1,1} + (x - 1) \times f_{i-1,0}

f i , 1 = ( S x ) × f i 1 , 0 + ( S x 1 ) × f i 1 , 1 f_{i,1} = (|S| - x) \times f_{i-1,0} + (|S| - x - 1) \times f_{i-1,1}

CF1097B Petr and a Combination Lock

给你一个圆形的密码锁,中间的指针初始在刻度 0 0 上,每次操作要旋转 a i a_i 度,你可以改变每次旋转是顺时针还是逆时针,问是否有方法可以使得最后指针回到 0 0 刻度上。

f i , j f_{i,j} 为前 i i 个数是否可以组成 g g 。则有
f 0 , 0 = 1 f_{0,0} = 1

f i , j = f i 1 , j a i f i 1 , j + a i f_{i,j} = f_{i-1, j - a_i} | f_{i-1,j+a_i}

CSPS 2019 Day2 T1 Emiya 家今天的饭

Link

AT693 文字列

Link

AT690 木

Link

猜你喜欢

转载自blog.csdn.net/qq_39984146/article/details/107745121