动态规划 笔记
动态规划 笔记
一、本质
动态规划的本质是状态压缩(by Scape)
状态压缩只和答案有关的值
所以dp是一个不断探索问题性质,减少和答案有关的值的个数
二、类似背包的dp
有多少不同的序列 和为\([Math Processing Error]n\) 并且任意两个数\([Math Processing Error]\mod m\)都互不相同
两个序列不同当且仅当他们长度不同或者某个位置上的数不同
\(m \leq 100\)
首先我们发现 序列的长度最多为m 因为他们\(\mod m\)两两不同
那么我们可以把这最多m个数全部设定为0~m-1之间 最后在在某一些上面加上m的倍数 一直加到n
这样我们可以设计出一个状态 \(f[i][j][k]\)表示当前考虑到\(i\)这个数 当前选了\(j\)个数 当前选的数的和\(\mod m=k\)的方案数
那么我们呢可以做出一个\(O(n^4)\)的dp
计算答案:
后面的组合数是用隔板法做出来的
SRM 625
\(n\)个座位的圆桌 \(k\)个人坐 任意时刻联通块数量\(\leq G\)
求方案数 \(n,k,G \leq 2000\)
先瞎写一个dp
令\(f[i][j]\)表示坐了\(i\)个人 有\(j\)个联通块的方案数
那么考虑三种情况
- 一个人坐进去 左右都没有人 \(f[i][j]+=f[i-1][j-1]\)
- 一个人坐在一个联通块的边上 联通块个数不变 \(f[i][j]+=f[i-1][j]\times j\)
- 一个人把两个联通块合了起来 \(f[i][j]+=f[i-1][j+1]\times j\)
结果就对了
这样转移为什么就可以了呢
我们把人坐下去转化成在人之间插空格
那么如果当前有\(j\)个联通块并且总座位数-总人数\(\geq j\)
那么一定有一种插空格的方式
所以我们在dp的时候 按照上面的方程式dp 并且记得在联通块数量\(\gt G\) 或者总座位数-当前人数\(\lt j\)的时候不计算 就可以了
当然这不是答案 因为空格的位置可以自己定 所以\(i\)个人 \(j\)个联通块 那么我们还有\(n-i\)个空格 需要放进\(j\)个联通块之间的缝隙中 那么就是\(C_{n-i-1}^{j-1}\)种放法 乘上方案数就好了
复杂度\(O(kG)\)
BZOJ2287
ftiasch 有 N 个物品, 体积分别是 W1, W2, …, WN。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N – 1 物品装满容积为 x 的背包,有几种方法呢?” — 这是经典的问题了。她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格。
如果没有去掉一个物品的条件 那么就是一个经典的背包问题
这样的问题我们称为"退背包"
首先还是找出dp数组
\(f[i][j]\)表示前\(i\)个物品 占用容积为\(j\)的方案数
\(f[i][j]=f[i-1][j]+f[i-1][j-w[i]]\)
现在我们需要第\(i\)个物品不选
那么我们用总数-第i个物品选的方案数
所以我们用\(c[i][j]\)表示题目中的\(count(i,j)\)
分类讨论
- \(j \geq w[i]\) 这个时候\(c[i][j]=f[n][j]-c[i][j-w[i]]\)
- \(j \lt w[i]\) 这个时候\(c[i][j]=f[n][j]\) 肯定不会用到第\(i\)个物品
那么就做完了
有 \(N\) 个点, 每个点有一个权值 \(A_i\) . 现在要把这 \(N\) 个点连成一棵树, 设第 \(i\) 个点在树中的度数为 \(d_i\) , 那么这棵树的权值为 \(\prod^N_{i=1} {{A_i}^{d_i}}\) .
求所有可能的树的权值之和模 \(10^9 +7\) 的结果. (\(N ≤ 2000, Ai ≤ 1000000\)).
首先一堆点连成一棵树的所有情况可以想到prüfer序列
我们要求
中间那个分数就是这种度数序列出现的种数
我们用\(f_{i,j}\)表示当前考虑前\(i\)个点 前\(i\)个点的度数和为\(j\)的对应的上面式子的值
那么枚举当前点的度数来转移 可以做到\(O(n^3)\)
怎么优化呢?
讲道理想不到...
bzoj 4753
\(N\)个人 每个人都由一个编号比他小的人推荐 选\(K\)个人 满足如果选了\(A\) 那么推荐\(A\)的人也得选 每个人有两个值 价值和代价 求总价值与总代价的比值的最大值
这题很好做 首先分数规划 二分答案之后 每一个人都用他的价值-代价×当前答案
然后这题就变成了选一些人 满足...条件 并且每个人都有一个权值 求是否能选出一些人 这些人的权值之和>0
这是一件很好做的事情
令\(f[i][j]\)表示当前考虑到\(i\)号点 以\(i\)为根的子树中选\(j\)个点且满足要求的最大权值
那么更新也很容易 \(f[i][j]=max(f[i][j],f[i][j-k]+f[nwson][k]\)
这是\(O(n^2 \log n)\)的 可以通过这道题
SRM 613
给一个棋盘染色 每列最多染一次 每行所有列数\(\leq L[i]\)的格子总共被染一次 每行所有列数\(\geq R[i]\)的格子总共只被染一次 问方案数
这样就搞定了