题目链接:
题目大意:
给定一个数组 x, x 的各项均为正整数,设在原点处开始, x[0] 代表向北移动的距离, 然后, x[1] 代表向西移动的距离, x[2] 代表向南移动的距离, x[3] 代表向东移动的距离, x[4] 代表又一次向北移动的距离 ... 依此类推, 也即, 按逆时针方向逐次移动 ;
写出 空间复杂度为 O(1) 且为 one-pass 的算法, 以判断移动的路径是否存在交叉 ;
例如: 给定数组 [2, 1, 1, 2], 则输出应为: true ;
例如: 给定数组 [1, 2, 3, 4], 则输出应为: false ;
例如: 给定数组 [1, 1, 1, 1], 则输出应为: true ;
解题过程:
(1) 由于题目的要求, 我只能想到 穷举所有的可能导致相交(或不相交)的情况并进行逐一判断 的方案, 而且只能利用有限( O(1) )的信息进行判断而不能持续保存信息, 不过具体需要逐一判断的方案数则确实有可能抽象得尽量少 ;
(2) 显然, 理想的方式是一旦发现有交叉, 即马上返回 true ;
(3) 发现 只有当 移动次数 >= 4 时才有可能发生交叉; 且特定的方向上的移动也只可能在特定的情况下和特定方向上的轨迹产生交叉; 故考虑, 针对 4个方向的各个情况, 分别进行判断 ;
(4) 但是发现逻辑写起来太乱, 又考虑到 移动模式固定不变, 因此可以认为对给定的一次移动 m 来说, 决定其是否与其他边发生交叉的因素与其当前移动方向无关(也即, 可以一直以其当前移动方向作为 "前方") ;
(5) 在前基础上, 又考虑到 对给定的一次移动 m 来说, 其只可能与其之前的特定的轨迹发生交叉, 因为超过一定次数 (n 次, 后发现 n = 6, 且实际上也不需要检测与倒数第 6 个轨迹(的相切)) 之前的轨迹会呈现 "被包围" 状态, 不存在与 当前移动轨迹交叉的可能 ;
(6) 于是考虑直接写死 (代码中, "i - 1" 即表示倒数前一次的移动, "i - 2" 即表示倒数第一次的移动, ... 依此类推 ;
代码如下:
bool crossLast3Track( vector< int > & x, int trackIndex ) {
auto i = trackIndex;
return x[i] >= x[i - 2] && x[i - 1] <= x[i - 3];
}
bool crossLast4Track( vector< int > & x, int trackIndex ) {
auto i = trackIndex;
if ( i < 4 ) {
return false;
}
return x[i - 1] == x[i - 3] && x[i] >= x[i - 2] - x[i - 4];
}
bool crossLast5Track( vector< int > & x, int trackIndex ) {
auto i = trackIndex;
if ( i < 5 ) {
return false;
}
return x[i - 2] >= x[i - 4] && x[i] >= x[i - 2] - x[i - 4] &&
x[i - 1] <= x[i - 3] && x[i - 1] >= x[i - 3] - x[i - 5];
}
bool isSelfCrossing( vector< int > & x ) {
for ( int i = 3; i < x.size(); i++ ) {
if ( crossLast3Track( x, i ) ||
crossLast4Track( x, i ) ||
crossLast5Track( x, i ) ) {
return true;
}
}
return false;
}
Runtime: 0 ms