Gosper's Hack 算法
作者:互联网
XIN 队算法之枚举组合 .
枚举组合的一个非递归做法叫 Gosper's Hack 算法,思路就是对每个组合,用 01 串表示其选或不选,这样必然可以表示所有组合 .
我们考虑如何生成一个组合的下一个组合,因为是组合,所以我们要保证串串的 popcount 不变,这样就考虑把最后一个 01 变成 10,这样显然是对的 .
而且要保证生成完的串串 \(b'\) 和原来的串串 \(b\) 的差值 \(b'-b\) 最小,这样我们才能保证生成所有组合,于是我们只需要把所有 1 集中到最右边即可 .
我们考虑如何用位运算描述这个过程,具体可以看下面的代码手玩一下 .
比如按字典序生成 \(n\) 元集合所有 \(k\) 元子集就可以写为
const int N = 1 << 25;
int n, k, state[N], cc;
int main()
{
scanf("%d%d", &n, &k);
if (!k) return 0;
int cur = (1 << k) - 1, limit = (1 << n);
while (cur < limit)
{
state[++cc] = cur;
int lb = cur & -cur, r = cur + lb;
cur = ((r ^ cur) >> __builtin_ctz(lb) + 2) | r;
// 如果不用 builtin 函数可以写 cur = (((r ^ cur) >> 2) / lb) | r;
}
for (int i=cc; i>=1; i--, puts(""))
for (int j=n-1; j>=0; j--)
if (state[i] >> j & 1) printf("%d ", n-j);
return 0;
}
时间复杂度为 \(\displaystyle O\left(\dbinom nk\cdot n\right)\),非常的高效,也非常的简洁 .
标签:Gosper,组合,串串,int,生成,算法,Hack,lb 来源: https://www.cnblogs.com/CDOI-24374/p/16642647.html