混乱的奶牛 [Don Piele, 2007]
Farmer John的N(4 <= N <= 16)头奶牛中的每一头都有一个唯一的编号S_i (1 <= S_i <= 25,000). 奶牛为她们的编号感到骄傲, 所以每一头奶牛都把她的编号刻在一个 金牌上, 并且把金牌挂在她们宽大的脖子上. 奶牛们对在挤奶的时候被排成一支"混乱"的队伍非常反感. 如果一个队伍里任意两头相邻的奶牛的 编号相差超过K (1 <= K <= 3400), 它就被称为是混乱的. 比如说,当N = 6, K = 1时, 1, 3, 5, 2, 6, 4 就是一支"混乱"的队伍, 但是 1, 3, 6, 5, 2, 4 不是(因为5和6只 相差1). 那么, 有多少种能够使奶牛排成"混乱"的队伍的方案呢?
N<=16 状压dp
很显然有一个O(n!)的算法,尝试优化它。
考虑算法执行过程中的冗余计算。
假设当前已经枚举了左边i位的牛,并且出现的牛的集合已经确定(即知道哪些牛已经用过了),
那么除了第i位的牛之外,前面牛的排列已经影响不到后面的选择了。
而O(n!)算法中,这样的所有合法情况下,都会暴力枚举一遍后面的所有决策,浪费时间。
考虑把所有等价的状态放到一起计算→状态压缩动态规划
令dp[mask][j]表示已经出现的牛的集合为mask(用二进制数描述一个集合),
并且最右的奶牛为j的合法排列方案数。转移时枚举下一个选择的奶牛。
状态数O(n*2^n),转移O(n),总复杂度O(n^2*2^n)
要思想:O(n!)→O(2^n)
用一个n位二进制数表示一个子集。
第i位表示第i个元素是否取。
(假设位从0开始编号)显然0到2^n-1的数恰好一一对应了每个子集。