【问题标题】:Array Recursion数组递归
【发布时间】:2011-02-25 17:36:29
【问题描述】:

我有一个我不知道的任务,任何指针将不胜感激,它是这样的:

有一系列灯泡表示为真/假数组,每个灯泡都有一个开关,通过单击任何灯泡的开关,您可以切换它以及 2 个相邻的灯泡(左起 1 个,另一个 1从右边开始;如果在边缘单击开关的灯泡——当然只有 1 个相邻的开关)。

需要完成的是一种方法,该方法接受一系列打开/关闭的灯泡阵列和另一个表示假设在单击某些开关后的第一个阵列的另一种状态的方法。 .! 因此必须使用递归来找出是否存在将数组 1 转换为数组 2 的开关单击组合。

这是方法的签名:

public static boolean disco(boolean[] init, boolean[] target)

如果数组 init 可以转换为 target,则返回 true,否则返回 false。 方法必须是静态的,不能使用 循环 和任何其他静态和全局变量,只能使用本地变量。

例子:

boolean[] init = {true, false, true, false, true, false};
boolean[] target = {false, true, false, true, false, true};

对于以上 2 个数组,disco(init, target) 将返回 true,因为切换第 1 个和第 4 个灯泡会产​​生目标状态(记住相邻的灯泡也会被切换)。

【问题讨论】:

  • 不要只发布作业,告诉我们您尝试过什么或正在考虑采用什么方法。我们不是作业完成引擎。
  • 如果这是作业,请添加homework标签。
  • 我想如果您切换第一个和第二个灯泡,即如果一个相邻的灯泡不可用,则没有环绕或类似的东西。对吗?
  • 由于两侧相邻的灯泡也被切换,我猜不是所有的灯泡都需要切换,但只有 1+3n。换句话说,每个灯泡都有两个相邻的灯泡(即不是第一个和最后一个)和相邻灯泡不是另一个灯泡的相邻灯泡的灯泡......哈哈。我尝试切换给定示例中的每个灯泡,但没有给出正确的结果。
  • 另外,对于达到基本情况,分配中给出的签名没有用于保存当前索引的参数,我想到了两种方法:每次传递一个新的较小的 init 数组和检查它的长度是否为 1... 等等,但我需要完整的 init 数组来品尝所有值,不是吗?其他方法是使用所需参数重载方法。你怎么看?我想我错过了这里的重点......>_>

标签: java arrays algorithm recursion


【解决方案1】:

新版本

public static boolean disco(boolean[] init, boolean[] target)  
{  
  return recurse(init,boolean,0);  
}  

public static boolean recurse(boolean[] init, boolean[] target, int min)  
{  
  if (min == init.length)  
    if (init == target)  
      return true;  
    else  
      return false;  
  boolean[] temp = "init with a change at min";  
  boolean a = recurse(init, target, min+1);  
  boolean b =   recurse(temp, target, min+1);  
  return a||b;
}  

新的新版本

我把它分为三种情况:

案例 1:长度 %3 = 0
通过更改第一个灯泡和第二个灯泡,您只能影响第三个灯泡的更改。 然后更改为 4 和 5 将使 6th 成为唯一更改的。我们看到我们可以改变每个灯泡的指数可以被 3 整除。
向后工作(从最后开始)我们可以做同样的事情,但这次它向我们展示了我们可以更改指数 (i+1) 可被 3 整除的灯泡。
将两者结合起来,我们看到如果我们想更改索引 0,1 mod 3 我们可以。但是要更改 2,我们只需更改相邻的 0,1 对,然后对中间的一对进行更改。所以在所有情况下,这些都是可以解决的。

案例 2:长度 %3 = 1
就像第一种情况一样,但是我们可以在 0,2 mod 3 处影响单个更改,从而挤压 1 mod 3 情况。

案例 3:长度 %3 = 2
向前和向后工作只会产生 case 0 mod 3。剩下的唯一动作是我们对灯泡进行了两次更改(因为我们可以忽略对位置 0 的任何更改)。更改 1 或 2 将反转由两个 0 包围的位置,而更改 0 将交换 1,2 相邻块中的奇偶性,它们之间有一个 0(如果绘制它会更容易)。但是到目前为止我所展示的是,0 都可以被纠正,并且在任何相邻的 1,2 中,如果它们都错误,您可以在不更改任何其他内容的情况下修复它们。如果一个错误,那么您将更改传播到相邻的一对 1,2。如有必要,可以移动此更改。这意味着可以修复 1,2 位置中的任何偶数个错误,但不能修复奇数个错误。位置 0 的所有错误都可以修复。

public static boolean disco(boolean[] init, boolean[] target)  
{  
  if (init.length%3 == 0 || init.length%3 == 1)
    return true;
  return recurse(init,target,0, true);
}

public static boolean recurse(boolean[] init, boolean[] target, int index, boolean even)
{
  if (index = init.length)
    return even;
  if (init[index] != target[index] && index%3 != 0)
    even = !even;
  return recurse(init, target, index+1, even);
}

【讨论】:

  • 你已经解决了 OP 的作业,一方面很好,但另一方面可能无法帮助他/她真正理解并自己解决问题。在极端情况下,他可以在没有真正学习编程的情况下毕业——然后有一天可能会成为你的同事......
  • 我忘了补充一点,根本不允许循环!非常感谢您的时间。
【解决方案2】:

所以这里是用文字我会怎么做:

如果只剩下少于或等于 3 个灯泡,则很容易检查它们是否可以切换以确保正确。

如果灯泡超过三个,请执行以下操作:

检查第一个灯泡是否正确。如果是这样,则从第二个灯泡开始递归地处理子数组。

如果第一个灯泡不正确,请切换第二个灯泡(切换第一个灯泡将不起作用,因为之前的灯泡会被切换回来)。现在,第一个灯泡是正确的。继续从第二个灯泡开始的子阵列。

【讨论】:

  • 唯一的问题是:如果第一个灯泡不正确,那么第一个灯泡可能已经翻转;如果第一个灯泡是正确的,则第一个和第二个灯泡可能都已翻转。如您所述,您必须递归检查两种可能性。
【解决方案3】:

提示:设 a1, a2, ..., ak, ..., an 为输入,b1, b2, ..., bk, ..., bn 为所需输出。

你可以递归地测试左右两边,然后组合结果。棘手的部分是合并结果。

从左侧开始。

  1. 测试 a1, a2, ..., ak, 对 b1, b2, ..., bk, true。
    1. 如果为真,则测试 a1、a2、...、ak 到 b1、b2、...、bk。
      1. 如果为真,则从 a1、a2、...、ak 到 b1、b2、...、bk 的解决方案不能包括切换第 k 个开关
      2. 如果为 false,则从 a1、a2、...、ak 到 b1、b2、...、!bk 的解决方案必须包括切换第 k 个开关
    2. 如果为假,则将 a1、a2、...、ak 测试为 b1、b2、...、bk
      1. 如果为真,则从 a1,a2, ..., ak 到 b1, b2 ,.., bk 的解决方案必须包括切换第 k 个开关
      2. 如果为 false,则从 a1,a2,...,ak 到 b1,b2,...,!bk 的解决方案不能包括切换第 k 个开关

在左侧进行两次测试后,您可以在右侧进行类似的测试。您将能够组合测试的值,因为您将知道第 k 个开关是否被切换。

【讨论】:

    【解决方案4】:

    感谢大家的帮助,这是我想出的:

    public static boolean disco(boolean[] init, boolean[] target)
    {
        if (java.util.Arrays.equals(init, target)) {
            return true;
        } else {
            return disco(init, target, 0);
        }
    }
    
    private static boolean disco(boolean[] init, boolean[] target, int i)
    {
        if (i > init.length-1) {
            return false;
        }
    
        disco(init, target, i+1);
        boolean t = false;
    
        if (init[i] != target[i]) {
            switchBulb(init, target, i);
        }
    
        if (java.util.Arrays.equals(init, target)) {
            t = true;
        }
    
        return t;
    }
    
    private static void switchBulb(boolean[] init, boolean[] target, int i)
    {
        if ( (i > 1) && (init[i-1] != target[i-1]) && (init[i-2] != target[i-2]) ) {
            // If there's a series of 3 opposite-to-target bulbs, switch the middle one & from both sides
            init[i-1] = !init[i-1];
            init[i] = !init[i];
            init[i-2] = !init[i-2];
        } else if (i > 0 && i < init.length-1) {
            // There's only 1 bulb or 2 adjacent bulbs that are opposite of target.
            if (i == 1 && (init[0] != target[0]) ) {
                // First 2 bulbs are opposite of target's, so switch the 1st one.
                init[i-1] = !init[i-1];
                init[i] = !init[i];
            } else if ( (i == init.length-2) && (init[i+1] != target[i+1]) ) {
                init[i+1] = !init[i+1];
                init[i] = !init[i];
            } else {
                init[i] = !init[i];
                init[i-1] = !init[i-1];
                init[i+1] = !init[i+1];
            }
        } else {
            // First bulb or last bulb.
            init[i] = !init[i];
            if (i == 0) {
                init[i+1] = !init[i+1];
            } else {
                init[i-1] = !init[i-1];
            }
        }
    }
    

    我觉得我在这里使用的递归很少,如果正确使用递归可能会更干净。 有什么想法吗? 我说的是 switchBulb 函数,它可以换成更复杂的递归逻辑,还是我错了?

    例如,如果您只是逐个迭代灯泡并与目标数组进行比较,则无需对所有灯泡组合都进行正确处理(请参见示例),那么您如何模拟随机灯泡切换递归?或者有模式吗?一定有,如果没有,它怎么知道什么时候停止?

    我想我得到了解决方案,如果有人感兴趣,稍后会发布......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-13
      • 2018-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 1970-01-01
      相关资源
      最近更新 更多