消除游戏
题目描述
列表 arr 由在范围 [1, n] 中的所有整数组成,并按严格递增排序。请你对 arr 应用下述算法:
从左到右,删除第一个数字,然后每隔一个数字删除一个,直到到达列表末尾。
重复上面的步骤,但这次是从右到左。也就是,删除最右侧的数字,然后剩下的数字每隔一个删除一个。
不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
给你整数 n ,返回 arr 最后剩下的数字。
思路
观察到经过几轮删除之后,序列依旧是是等差数列,并且相邻两个数的差值为2的n-1次方(n为轮次)。
并且正序删除后,首项一定会变为原首相加上公差,而末项则分为:偶数项,末项不变;奇数项,原末项被删除,变为原末项减去公差。
反序删除也类似,末项一定删除变为原末项减去公差,而首项分为:偶数项,首相不变;奇数项,原首项被删除,变为原首项减去公差。
所以每次删除之后可以算出首尾项,并且知道公差,也算是变相的知道了每次删除之后剩的序列到底是什么。
最后,根据如果首相和末项的值是否相等来判断序列是不是只剩下一个项了。
其实还可以将算法简化一些,就是由于每次删除之后,序列个数:n%2,所以知道首项、公差、个数也可以知道整个序列,所以没有必要再对末项进行过多的操作了,循环结束条件也可以改为由序列个数决定。
以下代码是未简化的版本。
具体实现
int lastRemaining(int n){
int num = n,i=1;
int first=1,end=n;
int turn = 0;//正向还是逆向
while(first!=end){
if(num%2==0){
if(turn==0){
first=first+i;
//end=end;
}else{
//first=first;
end=end-i;
}
} else{
first=first+i;
end=end-i;
}
num=num/2;
turn=(turn+1)%2 ;
i=i*2;
}
return first;
}