LeetCode赛题390----Elimination Game

390. Elimination Game


There is a list of sorted integers from 1 to n. Starting from left to right, remove the first number and every other number afterward until you reach the end of the list.

Repeat the previous step again, but this time from right to left, remove the right most number and every other number from the remaining numbers.

We keep repeating the steps again, alternating left to right and right to left, until a single number remains.

Find the last number that remains starting with a list of length n.

Example

Input:
n = 9,
1 2 3 4 5 6 7 8 9
2 4 6 8
2 6
6

Output:
6
算法分析

首先可以想到的最原始的一个方法就是构造一个列表,一遍一遍地来回删除其中的元素,直到列表里只剩下一个元素。
但这显然不是问题的设计的初衷。
首先设输入数据为 n 时,最终结果为 f(n). 很显然,有 f(1)=1,f(2)=2.
当 n=2k 时,从1,3,5,... ,2k-1 删除奇数项后,剩余的是2,4,6,... ,2k, 共 k 个偶数项。
当 n=2k+1 时,从1,3,5,... ,2k-1 ,2k+1 删除奇数项后,剩余的是2,4,6,... ,2k, 共 k 个偶数项。
这两种情况在从左到右删除一遍后,剩下的内容相同,所以最终剩下的一项也相同,所以我们可以得出结论:

f(2k+1)=f(2k)

所以,我们只需考虑 n 为偶数的情况。

假设 n=n1=k (k=1,2,3,...)时,f(n1)=m1,那么当n=n2=2*n1=2k (k=1,2,3,...)时,从左到右第一遍删除奇数后,还剩下 k 个偶数,此时从右往左删除,情况跟n=n1时从左往右开始删除类似,则最终剩下的是从 2k 往左数第 m1 个偶数,所以f(n2)=n2-2*(m1-1),即:

f(2k)=2k-2*f(k)+2

同理,有

f(2^(k+1))=2^(k+1)-2*f(2^k)+2

对上式两边同时除以 2^(k+1),有:

f(2^(K+1))/2^(k+1)= - f(2^k)/2^k +1/2^k + 1.  

g(k)=f(2^k)/2^k,则:

g(k+1)= -g(k)+1/2^k+1.  

由于上式两边函数项正负号不同,难以推出通项公式,所以我们需要进一步推理,有:

g(k+1)  
= -g(k)+1/2^k+1  
= -[-g(k-1)+1/2^(k-1)+1] + 1/2^k +1  
= g(k-1)-1/2^k  

且有 g(0)=f(1)/1=1, g(1)=f(2)/2=1.
则:
(i) 当 k=2m (m=1,2,3,...) 时,有:

g(2m)  
=g(2m-2)-1/2^(2m-1)  
=g(2m-4)-1/2^(2m-3)-1/2^(2m-1)  
=g(2m-6)-1/2^(2m-5)-1/2^(2m-3)-1/2^(2m-1)  
=...  
=g(0)-1/2^1-1/2^3-1/2^5-...-1/2^(2m-1)  
=1-(2-2/4^m)/3  
=(2/4^m+1)/3  

当取m=0时,g(2m)=1=g(0),所以,当 k=2m (m=0,1,2,3,...)时,g(2m)=(2/4^m+1)/3
(ii) 当 k=2m+1 (m=1,2,3,...) 时,有:

g(2m+1)  
=g(2m-1)-1/2^(2m)  
=g(2m-3)-1/2^(2m-2)-1/2^(2m)  
=...  
=g(1)-1/2^2-1/2^4-1/2^6-...-1/2^(2m)  
=1-(1-1/4^m)/3  
=(1/4^m+2)/3  

当取m=0时,g(2m+1)=1=g(1),所以,当 k=2m+1 (m=0,1,2,3,...)时,g(2m+1)=(1/4^m+2)/3

又因为 f(2^k)=2^k*g(k),所以,对于给定的 n ,当 n 为2^k时,我们可以根据k是奇数还是偶数得出f(n)的公式。即:
(i) 当 n=2^(2m) (m=0,1,2,3,...)时,有:

f(n)  
=f(2^(2m))  
=2^(2m)*g(2m)  
=n*(2/4^m+1)/3  
=(n+2)/3  

(ii) 当 n=2^(2m+1) (m=0,1,2,3,...)时,有:

f(n)  
=2^(2m+1)*g(2m+1)  
=n*(1/4^m+2)/3   
=(2n+2)/3  

所以,对于输入的 n ,如果是 1,我们直接返回1 。否则,判断其是否为偶数,如果不是,则将其做减1处理,因为 f(2k+1)=f(2k).此时保证n为偶数。
然后判断 n 是否为 2^k,如果是,则根据上面推理出的公式进行计算;如果不是,则递归计算 f(n/2) ,因为 f(n)=n-2f(n/2)+2.
代码如下:

Java算法实现

    public class Solution {
        public int lastRemaining(int n) {
                if (n == 1)
                    return 1;
                if (n == 2)
                    return 2;
                if ((n & 1) == 1) {
                    n--;//化为偶数,因为 f(2k+1)=f(2k)
                }
                if (((n - 1) & n) == 0) {
                    //为2的整数幂
                    int index = 0;
                    int tmpN = n;
                    tmpN = tmpN >>> 1;
                    while(tmpN > 0) {
                        //n=2^k,此处用 index 表示k的值
                        index++;
                        tmpN = tmpN >>> 1;
                    }
                    int ans = 0;
                    if ((index & 1) == 1) {
                        //index为奇数  
                        //对应f(n)=(2n+2)/3,此处 tmpN=2n  
                        tmpN = n << 1;
                    }
                    else {
                        //index为偶数
                        //对应f(n)=(n+2)/3,此处 tmpN=n  
                        tmpN = n;
                    }
                    ans = (tmpN + 2) / 3;  //tmpN=n 或 tmpN=2n
                    return ans;
                    
                }
                else {
                    //n不是2的整数幂,但目前已经能保证n为偶数,递归计算
                    int tmpAns = lastRemaining(n >>> 1);
                    int ans= n - (tmpAns << 1) + 2;
                    return ans;
                }
        }
    }
原文地址:https://www.cnblogs.com/dongling/p/5823911.html