当n个人坐在一个圆圈中时找到幸存者

时间:2015-01-08 21:15:52

标签: java algorithm arraylist

您好我遇到了这个问题并试图解决这个问题

花一点时间想象你在一个有100把椅子排成一个圆圈的房间里。这些椅子按顺序编号从一到一百。 在某个时间点,#1椅子上的人将被告知要离开房间。将跳过椅子#2中的人,并且将告知椅子#3中的人员离开。旁边去的是椅子#6的人。换句话说,最初会跳过1个人,然后是2,3,4 ......等等。这种跳绳模式将继续围绕圈子,直到只剩下一个人......幸存者。请注意,当人离开房间时,椅子会被移除。写一个程序来确定幸存者所在的椅子。

我取得了很好的进展但坚持了一个问题,在计数达到100并且不确定如何从这里进行迭代之后,任何人都可以帮助我,这是我的代码

import java.util.ArrayList;

public class FindSurvivor {

public static void main(String[] args) {
    System.out.println(getSurvivorNumber(10));
}

private static int getSurvivorNumber(int numChairs) {
    // Handle bad input
    if (numChairs < 1) {
        return -1;
    }

    // Populate chair array list
    ArrayList<Integer> chairs = new ArrayList<Integer>();
    for (int i = 0; i < numChairs; i++) {
        chairs.add(i + 1);
    }


    int chairIndex = 0;
    int lr =0;
    while (chairs.size() > 1) {
        chairs.remove(lr);
        chairIndex+=1;
        System.out.println(lr+" lr, size "+chairs.size()+" index "+chairIndex);
        if(lr==chairs.size()||lr==chairs.size()-1)
            lr=0;
        lr = lr+chairIndex;
        printChair(chairs);
        System.out.println();
    }

    return chairs.get(0);
}

public static void printChair(ArrayList<Integer> chairs){
    for(int i : chairs){
        System.out.print(i);
    }
}
}

5 个答案:

答案 0 :(得分:2)

答案是31.这里有三种不同的实现

var lastSurvivor = function(skip, count, chairs) {
    //base case checks to see if there is a lone survivor
    if (chairs.length === 1)
        return chairs[0];
    //remove chairs when they are left/become dead
    chairs.splice(skip, 1);
    //increment the skip count so we know which chair
    //to leave next. 
    skip = (skip + 1 + count) % chairs.length;
    count++;
    //recursive call
    return lastSurvivor(skip, count, chairs);
};

/** TESTS *******************************************************************
----------------------------------------------------------------------------*/

var result = lastSurvivor(0, 0, chairs);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31




/** ALTERNATE IMPLEMENTATIONS ***********************************************
-----------------------------------------------------------------------------

/* Implemenation 2 
-----------------*/

var lastSurvivor2 = function(chairs, skip) {
    skip++;
    if (chairs === 1)
        return 1;
    else
        return ((lastSurvivor2(chairs - 1, skip) + skip - 1) % chairs) + 1;
};

/** Tests 2 *******************************************************************/

var result = lastSurvivor2(100, 0);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31



/* Implemenation 3 
------------------*/

var chairs2 = [];
for (var i = 1; i <= 100; i++)
    chairs2.push(i);

var lastSurvivor3 = function(chairs, skip) {
    var count = 0;
    while (chairs.length > 1) {
        chairs.splice(skip, 1);
        skip = (skip + 1 + count) % chairs.length;
        count++;
    }
    return chairs[0];
};

/** Tests 3 *******************************************************************/

var result = lastSurvivor3(chairs2, 0);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31

答案 1 :(得分:0)

我不确定您的删除模式是什么,但我可能会将其作为圆形链表实现,其中第100个座位支架将连接回第一个座位支架。如果您使用阵列,则必须担心每次移除后重新组织座位。

答案 2 :(得分:0)

如果您的步骤是增量步骤,则可以使用以下代码:

    int cur = 0;
    int step = 1;
    while (chairs.size() > 1) {
        chairs.remove(cur);
        cur += ++step;
        cur %= chairs.size();
    }
    return chairs.get(0);

如果您的步骤固定为1,那么根据@Jarlax提供的解释,您可以在O(log n)时间内使用一行代码解决问题:

//for long values
public static long remaining(long numChairs) {
    return (numChairs << 1) - (long)Math.pow(2,Long.SIZE - Long.numberOfLeadingZeros(numChairs));
}

//for BigInteger values
public static BigInteger remaining(BigInteger numChairs) {
    return numChairs.shiftLeft(1).subtract(new BigInteger("2").pow(numChairs.bitLength()));
}

但是,如果您坚持使用ArrayLists,则代码不需要额外的变量。始终删除第一个元素,然后删除 - 然后添加列表末尾的下一个元素。然而,这是O(n)。

    while (chairs.size() > 1) {
        chairs.remove(0);
        chairs.add(chairs.remove(0));
    }
    return chairs.get(0);

答案 3 :(得分:0)

有优雅的分析解决方案:

让我们改变人数:#2 - &gt; #1,#3 - &gt; #2,...,#1 - &gt; #100(最后我们只需要将1减去&#34;修复&#34;结果)。现在第一个人保持相反或离开。假设圈中只有64个人。很容易看出,在第一次淘汰后,圈中的32个人将保留,编号将从#1开始。所以最终只剩下#1。

我们有100个人。在36人离开这个圈子之后我们最终会有64个人 - 我们知道如何解决这个问题。对于离开房间的每个人,一个人仍然存在,所以64人的圈子将从1 + 2 * 36 =#73(新#1)开始。由于在第一步改变索引,最终答案将是#72。

一般情况下,res = 2 *(N - nearest_smaller_pow_2)= 2 * N - nearest_larger_pow_2。代码很简单:

public static long remaining(long total) {
    long pow2 = 1;
    while (pow2 < total) {
        pow2 *= 2;
    }
    return 2*total - pow2;
}

此算法也具有O(log(N))复杂度而不是O(N),因此可以计算巨大输入的函数(它可以很容易地适应使用BigInteger而不是long)。 / p>

答案 4 :(得分:0)

首先,让我们假设椅子从0开始编号。我们将在最后切换编号 - 但是当项目从0而不是1枚举时,通常情况会更简单。

现在,如果你有n个人并且你开始在椅子x上消除(x是0或1),那么在一次通过你就会消灭一半的人。然后你有一个大约一半大小的问题(可能加一个),如果你解决了这个问题,你可以通过将该子结果乘以2并可能加一个来构造原始问题的解决方案。

要对此进行编码,只需将4个案例(n个奇数或偶数对x 0或1)设为正确。这是一个通过使用按位技巧获得4个案例的版本。

public static long j2(long n, long x) {
    if (n == 1) return 0;
    return j2(n/2 + (n&x), (n&1)^x) + 1-x;
}

现在可以编写一个编号从1开始并且没有额外参数的解决方案:

public static long remaining(long n) {
    return 1 + j2(n, 0);
}

这在O(log n)时间运行并使用O(log n)内存。