状压 dp 做题记录

P2704 [NOI2001] 炮兵阵地

  • 可以发现 M10 ,那么考虑状压

  • 因为状态和前 2 行都有关,那么考虑状压下每两行的状态

  • f[i][S1][S2] ,表示当前 i 行,当前行状态为 S1 ,上一行状态为 S2 的方案数

P1896 [SCOI2005]互不侵犯

  • 可以发现 N9 ,考虑状压

  • 还是考虑压缩下每一行的状态

P1879 [USACO06NOV]Corn Fields G

  • 可以发现 M12 ,考虑状压

  • 还是考虑压缩下每一行

P3052 [USACO12MAR]Cows in a Skyscraper G

  • 可以发现 n18 ,考虑状压

  • f[S] 表示选这些物品的最小分组

  • 注意子集枚举的技巧 for(int S0=S;S0;S0=(S0-1)&S)

P2396 yyy loves Maths VII

  • 注意到 n24 ,还是考虑状压

  • f[S] 表示走到这个状态的方案数

  • 那么对于每个方案,枚举上一个出牌的那一张牌,注意厄运数字不能转移

P2831 [NOIP2016 提高组] 愤怒的小鸟

  • 注意到小猪 n18

  • 仍然是将小猪的状态压下来,枚举子集

  • 如果子集和自己小猪的数量差是 0/1 ,那么可以直接转移

  • 如果是多个小猪,那么就需要判断这些小猪能否用一个二次函数解决

  • 时间复杂度就是 O(Tn2n)

P2473 [SCOI2008] 奖励关

  • 可以发现这个东西不好正着推,因为有一些限制,所以考虑反着推

  • f[i][S]i 轮前,得到的物品集合为 S ,第 i 轮到第 n 轮得到的价值的期望

  • 那么枚举这一轮会得到什么物品,每个物品得到的概率是相等的,所以转移就可以了

  • 因为我们已经知道之前的状态了,所以这样转移是正确的

P3959 [NOIP2017 提高组] 宝藏

  • 其实就是对于这个图,求一个最小代价的生成树

  • 可以发现每次拓展的花费和所在的树高有关

  • f[S][i] 表示当前已经选好了集合 S,树高为 i 的最小代价

  • 我们可以枚举这个集合的子集,并且满足这个子集可以拓展得到这个集合,然后通过子集转移过来

  • 具体的对于每一个新加入的点,求出到这个子集的最短距离和,然后乘上子集的树高,也就是 f[S][i]=max(f[s0][i1]+idis)

  • 初始化就是每个点一开始做单独一个集合的代价就是 0

  • 可以发现有值的 f ,一定是至少满足这个集合是一联通的,不至于不合法

  • 但是对于这个树高不一定能达到,求出来的代价会偏大,但是其实这个是没有关系的

  • 因为最终的答案一定可以通过一个最优的形式转移过去,也就是答案其实是一定可以转移到的

  • 枚举每个集合的子集,复杂度是 O(3nn2)

P2150 [NOI2015] 寿司晚宴

  • 一个朴素的 dp 式子: dp[i][S1][S2] 表示吃完了当前的后,分别的质数集合

  • 但是因为值太大了,质数是压不下的,考虑一个经典的 trick ,根号分治

  • 对于 500 的质数,仍然状压,其他的质数单独考虑,每个数只会有一个大质数

  • 那么我们预处理出一个数的质数集合和大质数

  • 将所有的数按照大质数排序,这样做是为了将大质数相同的数放在一起考虑

  • 对于一段连续的大质数相同的数,这一段数要么全部给 S1 ,要么全部给 S2 ,要么谁都不要

  • 那么对于这一段可以单独用两个数组 f1[S1][S2],f2[S1][S2] 转移,分别表示这一段都给了 1,2

  • 那么在一段的开头,将 dp 数组赋值在 f1,f2 ,然后用 f1,f2v 转移,在一段结束后,再将 f1,f2 赋值回 dp 数组

  • 参考代码

P2622 关灯问题II

  • 考虑对于每种灯的状态都状压下来,然后跑最短路

P3343 [ZJOI2015]地震后的幻想乡

  • 说一个最平凡的做法

  • 对于每条边的相对排名的概率是相同的,那么最暴力的做法就是全排列出每条边的排列,然后直接 Kruskal 判断到第几条边整个图能联通,假设是第 x 条,那么这条边的期望时间是 xm+1

  • 那么我们可以转换成求每一种 xm+1 的贡献,也就是恰好第 x 边使得图联通的概率

  • 将这个概率前缀和一下,假设为 Pi ,那么恰好为第 i 条边使得图联通的概率就是 PiPi1

  • 所以现在就是要求 i 条边使得图能联通的概率(注意没有恰好

  • 而概率可以转换成求满足条件的方案数和方案总数,方案总数比较好表示,所以就是要求方案数

  • 注意到这个 n10 ,那么考虑状压,设 f[S][i] 表示 S 这个集合用 i 条边使得联通的方案数,设 g[S][i] 表示 S 这个集合用 i 条边无法联通的方案数,sz[S]S 集合涉及的边数量

  • 那么 f[S][i]+g[S][i]=(sz[S]i),正反则难,考虑求 g

  • 对于一个集合 S 的联通情况,我们考虑随便找一个集合内的点,然后枚举这个点所在的集合,这个集合用了的边的数量

  • g[S][i]=TS,TSj=0min(sz[T],i)f[T][j](sz[S]sz[T]ij)

  • g 求出来了,f 就可以求了

  • 那么 Pi=g[U][i](mi)

  • Ans=1m+1i=1mi×Pi

P5643 [PKUWC2018]随机游走

  • 题目就是要求到达一个集合期望最长的步数

  • 通过 Min-Max 转换成一个集合到一个点的最小期望步数

  • 用树上高斯消元预处理出每个集合到 x 的最小期望步数

  • 进一步的优化就是 FWT 卷一下

BZOJ2004交通路线 bus

  • 观察到这个 n109 ,考虑矩乘

  • 那么首先要推式子,发现这个 P,K 很小,把这个东西状压一下

  • f[i][S] 表示位置 [i,i+P1] 是状态 S 的方案数

  • 考虑怎么转移

  • 这个状态一定要恰好有 K 个 1

  • 对于每个合法的状态,考虑可以转移到哪些状态

  • 对于一个新的位置,一定会有一个位置跳过去

  • 如果当前状态的首位是 1 ,那么必须是这个 1 跳过去

  • 否则的话,就选一个 1 跳过去

  • 那么系数矩阵就可以预处理出来了

一些小 trick

  • 对于数据范围小成指数级别的,那就可以考虑状压了

  • 枚举每个集合的子集的复杂度是 3nfor(int S0=S;S0;S0=(S0-1)&S)

posted @   Kzos_017  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示