循环一下bitmask

时间:2016-12-07 22:55:37

标签: algorithm bitmask

这是对TopCoder SRM 466 "Lottery Ticket" problem的提交。我已经看到这种模式多次用于此问题。

  

尼克喜欢玩彩票。单个彩票的成本是价格。尼克正好有四张价值为b1b2b3b4的钞票(某些值可能相等)。他想知道是否有可能在没有任何改变的情况下购买单张彩票。换句话说,他想用他的钞票的任何子集支付票的确切价格。返回"可能"如果可能或"不可能"如果不是(为清晰起见,所有引号)。

string buy(int p, int b1, int b2, int b3, int b4) {
    int arr[] = {b1, b2, b3, b4};
    for (int msk = 0; msk < (1 << 4); ++msk) {
        int sum = 0;
        for (int i = 0; i < 4; ++i) {
            if (msk & (1 << i)) {
                sum += arr[i];
            }
        }
        if (sum == p) return "POSSIBLE";
    }
    return "IMPOSSIBLE";
}

有人可以解释这是如何工作的吗?我不明白为什么他将值放入数组并使用两个嵌套for循环进行循环。

3 个答案:

答案 0 :(得分:1)

对于每张纸币,您有两种选择,接受或离开(打开或关闭)。 使用4个音符,您可以将它们视为4位,如果以二进制形式通过0000到1111,则可以选择拾取它们的所有可能组合。

这就是比特矢量的作用。外部循环生成所有可能的子集,内部循环评估子集以查看它是否与所需的总和匹配。

答案 1 :(得分:1)

它只生成所有16种可能的组合。每个组合用4位表示,1表示使用钞票,0表示不使用钞票。

然后计算组合的总和,如果总和正确,则打印“可能”。

答案 2 :(得分:1)

这个问题可以扩展到任意数量的钞票,但让我们来看看这个例子。

这个解决方案的想法是使用强力方法来解决问题。这意味着我将尝试所有可能的解决方案,如果其中一个正在运行,那么结果是积极的。

在这种情况下,工作解决方案意味着我所选择的银行票据总和等于p

让我们先看看这段代码:

for (int msk = 0; msk < (1 << 4); ++msk)

这表示我将重复从02^4-1的所有数字,即0-15

如果你用二进制表示法编写这些数字,你会注意到它们涵盖了长度为4的所有可能组合(我们不必写入所有前导零,但它实际上总共有32位用于类型int)。

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

让我们选择其中一个例子,例如: 1010。这意味着我将在13位置选择数字(从右到左依次为0)。然后我将检查这两个数字的总和是否等于p

下一个for循环对具有1的位置的所有数字求和:

for (int i = 0; i < 4; ++i) {
    if (msk & (1 << i)) {
        sum += arr[i];
    }
}

如果我们分解它,那么我们有msk代表我们正在检查的当前组合,而(1 << i)只是向左移位按位操作,它给我们2^i,或者用二进制表示法:

0001 = 1 << 0
0010 = 1 << 1
0100 = 1 << 2
1000 = 1 << 3

注意:(1 << i)位于括号内,因为&具有更高的优先级,在这种情况下我们不希望这样。

如果在两个整数之间使用&运算符,则会得到按位运算,例如

1010 & 1000 = 1000   // this is greater than 0
1010 & 0100 = 0000   // this is equal to 0

因此if (msk & (1 << i))仅适用于当前组合中1的位置,即msk

我希望这也解释了为什么他将值放在数组中的原因 - 它是因为他想为每个钞票分配一个索引,然后如果掩码有1则使用该注释位置,而不是弄清楚应该使用哪四个变量。