【问题标题】:FInd the minimum number of switches to turn on all bulbsF找到打开所有灯泡的最小开关数量
【发布时间】:2016-03-08 05:16:05
【问题描述】:

我正在尝试了解here 给出的问题及其解决方案:

问题说明:

N个灯泡用电线连接。每个灯泡都有一个与之关联的开关,但是由于接线错误,一个开关也会改变当前灯泡右侧所有灯泡的状态。给定所有灯泡的初始状态,找出打开所有灯泡所需按下的最少开关数。您可以多次按下同一个开关。

注意:0代表灯泡关闭,1代表灯泡打开。

Example:

Input : [0 1 0 1]
Return : 4

Explanation :

press switch 0 : [1 0 1 0]
press switch 1 : [1 1 0 1]
press switch 2 : [1 1 1 0]
press switch 3 : [1 1 1 1]

给出的答案之一是:

int solve(int A[], int N) {

    int state= 0, ans = 0;
    for (int i = 0; i < N;i++) {
        if (A[i] == state) {
            ans++;
            state = 1 - state;
        }
    }

    return ans;
}

我似乎无法理解 if 语句是如何做正确的事情的。

【问题讨论】:

  • 请附上描述问题的简明图片的文字描述。
  • 实际上翻转所有灯泡的状态会非常昂贵。相反,我们只是假装灯泡的状态被翻转了。如果假装灯泡关闭,则真正的灯泡打开,反之亦然。如果我们想假装翻转一堆假装灯泡,我们最终会假装假装,这让我们回到现实。 state 记录我们是否在伪装。

标签: java algorithm bit-manipulation


【解决方案1】:

每当我们拨动一个开关时,我们都会将所有开关都向右拨动,所以如果我们正在搜索 0(关闭开关),现在我们需要搜索 1(打开开关),因为我们已经将所有开关都向右拨动了,让我们看一个例子: 0 1 1 0,现在初始状态 = 0,当我们遇到第一个灯泡时,我们翻转所有开关,即 1 0 0 1 但在数组中的值仍然是 0 1 1 0,所以我们需要能够识别由于上一次翻转,第二个索引处的灯泡被关闭,因此我们将状态更改为 1 - 状态,这使得我们正在寻找的状态为 1,类似地翻转开关会改变我们下一个状态的标准会搜索。

我在下面写了一段代码sn-p,这样会更容易理解

bool flipped = false;
int ans = 0;
for(int i = 0;i < N;i++){
    if(flipped == false){
        if(A[i] == 0){
            ans++;
            flipped = true;
        }
    }else if(flipped == true){
        if(A[i] == 1){
            ans++;
            flipped = false;
        }
    }
}

这里我使用了翻转变量来判断灯泡是否已翻转,如果它们已翻转,那么我们将搜索 1(打开开关),因为实际上它们是 0(关闭),因为之前的翻转。

【讨论】:

  • 为什么在这个问题中从左到右给出最佳解决方案,它可以从中间的某个 1 开始翻转导致更好的结果吗?
  • @user1846749:你问这个问题太久了,但我已经在同一个问题的单独答案中澄清了这一点。
【解决方案2】:

只有两点需要理解:

  1. 由于右侧的灯泡在翻转特定开关时会翻转,因此从左到右迭代灯泡是有意义的,即索引 0bulbs.length;而且,

  2. 因为右边灯泡的状态是可以反转的,所以我们需要知道是把灯泡的状态当作倒置处理还是照原样处理。


这是实现上述两点的简短代码。阅读cmets,它会超级简单理解:

int count = 0;
boolean flip = false; //Initially we don't flip the states, so flip is false

for(int i = 0; i < bulb.length; i++) {
    //A bulb is ON when:
    //1. The bulb is ON and it is not supposed to be flipped.
    if (bulb[i] == 1 && !flip) continue;

    //2. The bulb is OFF but it is supposed to be flipped.
    if (bulb[i] == 0 && flip) continue;

    //Now the bulb is OFF, so we turn it ON i.e. increment count and
    //invert the flipping condition for the rest of the bulbs on the right.
    count++;
    flip = !flip;
}

return count;

【讨论】:

  • 您以最简单的方式解释了它。从逻辑上讲,与我们在实践中应该采用的方式更相关
  • 以下测试用例有助于更快地找到此解决方案。 #1 [1, 1, 1, 1] if (bulb[i] == 1 &amp;&amp; !flip) continue; 语句可以由此形成。 #2 [0, 1, 0, 1] 或任何带有 0 和 1 的东西基本上可以帮助形成 if (bulb[i] == 0 &amp;&amp; flip) continue; 部分,其余的计数更容易更新。
  • @kushalvm:这种合乎逻辑的解释并非巧合。我在准备面试时解决了这个问题。所以当时我在逻辑上处理每一个问题。作为一名程序员,这是一个好习惯。我们以这种习惯编写的代码也更容易被其他开发人员理解。 :)
【解决方案3】:

另一个简单的解决方案:

int solve(int A[], int N) {

    int count=0;
    for (int i = 0; i < N;i++) {
        if ((A[i] == 0 && count%2==0)|| (A[i]==1 && count%2==1)) {
            count++;
        }
    }
    return count;

【讨论】:

    【解决方案4】:

    另一种理解解决方案的方法是分组,假设我们有 [0,0,0] 一个开关可以关闭或三个开关都可以关闭,所以我们将尝试找到这样的组。如果第一个灯泡亮着,我们不会翻转它。例如,

    1 0 1 0 0 0 1 1 0 0
    

    这里有 3 组零(灯泡关闭)和 3 组 1(灯泡打开)。但是对于第一个 1(ON 灯泡)我们不必关心。所以整体组 1 = 2 和组 0 = 3,因此我们需要 5 个开关来打开所有灯泡。

    int Solution::bulbs(vector<int> &A) {
        int group_one = 0;
        int group_zero = 0;
    
        if (A.size() == 0) return 0;
    
        if (A[0]) group_one++;
        else group_zero++;
    
        for (int i = 1; i < A.size(); i++) {
           if (A[i] == 0 && A[i - 1] == 1) group_zero++;
           else if (A[i] && A[i-1] == 0) group_one++;
        }
        // For All bulbs are either ON or OFF
        if (group_one && group_zero == 0) return 0;
        if (group_zero && group_one == 0) return 1;
    
        if (A[0]) group_one -= 1;
    
        return group_one + group_zero; }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-20
      • 1970-01-01
      相关资源
      最近更新 更多