1. 你面对一堵左右无限延伸的墙;
2. 该墙有且只有1扇门,它在离你 n 步远的地方;
3. 你不知道 n 的大小,也不知道门在你左边还是右边;
4. 你一次能够向左或者向右走一步,你只有走到门所在的位置才能出去。
请设计一个O(n)的算法,找到这扇门。
算法思路如下:
按一下顺序走:
先向左走1步,再向右走2步,再向左走4步....每次改变方向,且
每次走上一次的2倍。
坐标设置:人的起点为0,向右走表示正,向左表示负
设置参数k, n.
其中k表示第几次走
n是离远点(人起始点)的距离
如下:
第k次走 该方向上所走的步数 此时的坐标
第一次向右边走1步 1 = 2^0 1
第二次向左边走2步 -2 = -2^1 1+(-2)= -1
第三次向右边走4步 4 = 2^2 1+(-2)+4 = 3
第四次向左边走8步 -8 = -2^3 1+(-2)+4+(-8)= -5
第五次向右边走16步 16 = -2^4 1-2+4-8+16 = 11
...... ....... .............
第k-1次向左走 -2^(k-1) . .............
第k次向右边走 2 = 2^k 1-2+4-8+...- 2^(k-1)+2^k
2^k - 2^(k-1) + 2^(k-2) - 2^(k-3) .... = 走完第k次时的坐标
当 abs(2^k - 2^(k-1) + 2^(k-2) - 2^(k-3) ....) >= abs(n)
且 abs (2^(k-2) - 2^(k-3) + 2^(k-4) - 2^(k-5) ....) < abs(n)
的时候刚能找到门,其中因为n有正负,所以用到abs()。
此时一共走了 S = 2^k + 2^(k-1) + 2^(k-2)+ .... = 2^(k +1) - 1 = 2^(k+1) (-1零头不算)
这里我们假设n>0,即门在右边,此时就不要abs了(省去了abs)
因为 2^(k-2) - 2^(k-3) + 2^(k-4) - 2^(k-5) .... < n 。。。。1式
所以有
4 * 1式也就是
2^k - 2^(k-1) + 2^(k-2) - 2^(k-3) .... < 4n 。。。。2式
再1式*8有
2^(k+1) - 2^(k) + 2^(k-1) - 2^(k-2) .... < 8n 。。。。3式
前面2式和3式相加得出 2^(k+1) < 12n = O(n)
当n<0的时候,同理证明。
综上所得 S = 2^(k+1) < 12*N= O(N) 其中k为第几次走,N为距离(即N = abs(n) )
/*************************************************
* 算法思路: *
* 先向左走1步,再向右走2步,再向左走4步.... *
* 每次改变方向,且每次走上一次的2倍。 *
*************************************************
* 建立坐标: *
* 人的其实位置为0,人的右边正,左边为负 *
*************************************************/
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int sum; //一共走了多少步,即就是最后的复杂度O(n)
int zuobi; //存储当前坐标值
int step; //当前要走的步数
int flag; //flag表示方向,1:右边 -1: 左边
int n; //n是输入数,门所在坐标的位置
int k; //记录第几次走
//循环输入n
while(cin >> n)
{
//初始化
sum = 0;
step = 1; //开始时只走一步
flag = 1; //先往右边走
zuobi = 0; //开始的坐标为0
k = 0;
//先右走,再左走,再右,再左...所以是个循环
while(1)
{
//右边方向走
if(flag > 0)
{
k++;
sum += step; //总走步数
zuobi = zuobi + flag*step; //坐标值
cout << "第" << k << "次要往右走-> " << step
<< "步, 走完后的坐标: " << zuobi << endl;
//方向改变
flag *= -1;
//步数增倍
step *= 2;
}
else
if(flag < 0) //左边方向
{
sum += step; //总走步数
zuobi = zuobi + flag*step; //坐标值
cout << "第" << k << "次要往左走<- " << step
<< "步, 走完后的坐标: " << zuobi << endl;
//方向改变
flag *= -1;
//步数增倍
step *= 2;
}
//n*zuobi >0 是表示门door 和 此时的坐标在一个方向上
//在该方向走完,刚好找到了门
if( (n*zuobi>0) && (abs(n) == abs(zuobi)))
{
cout << "人一共走了: " << sum << "步找到了门!\n"<< endl;
break;
}
else//在该方向上还未走完就找到门
if( (n*zuobi>0) && (abs(n) < abs(zuobi)))
{
//减去未走完的步数
sum = sum - (abs(zuobi) - abs(n));
cout << "人一共走了: " << sum << "步找到了门!\n"<< endl;
break;
}
}
}
return 0;
}
6113

被折叠的 条评论
为什么被折叠?



