Solution
审题一定要仔细! 假如我们选出一个子序列 a1, a2, ...
, 那么对于任意的ai, aj
, 均满足|ai - aj| != K
;
一种很简单的算法是: 对数组进行排序, 然后二进制状态枚举, 对于一个数a
判断a - K
是否存在, 时间是: (bit_st: 2^20) * (N: 20)
;
看似可以, 但实际上会超时… (而且, 即便你把N: 20
使用Lower_bit
优化为<N
, 还是会超时), 只能说 这个题 卡时间卡的比较极限;
@Delimiter
当遇到2^n
的二进制枚举时, 应该联想到 DFS
递归, 两个算法是完全一样的;
但唯一的区别是: 相比于二进制枚举, DFS递归更有优化/剪枝的空间;
.
比如, 一组方案 (子序列) a, b, c
;
.
.
如果是二进制枚举 他一定是 比如st = 100101
, 然后如果你还需要遍历这个子序列, 那么总时间一定是2^N * N
, 没有任何可能去优化;
.
.
但如果是DFS递归, 当我们在a b
时, 假如他是非法的, 那么我们就不会访问到a b c
这种方案, 在a b
时 就已经给剪枝掉了;
.
.
.
换句话说, DFS递归它是: 2^N
遍历所有方案 和 N
遍历一组方案, 两个过程是结合到一块了 同时进行, 即时间是2^N
;
我们同样排序, 然后判断cur - K
这个元素是否已经选择;
先看一个错误代码:
void Dfs( int _ind){
if( _ind >= N){ ++ Ans; return;}
//--
int _cur = (* AA)[ _ind];
//--
Dfs( _ind + 1); // 不选当前元素
//--
if( _cur - BB >= 0 && Cont[ _cur - BB] == 0){ // @Mark_0
Cont[ _cur] ++;
Dfs( _ind + 1); // 选当前元素
//--
Cont[ _cur] --; // 递归回溯恢复
}
}
答案是`Ans - 1`
很容易写出这个代码, 而且很难察觉到错误…
举个例子, [1] K=5
; 你并不会得到1
这个方案, 因为1
不符合@Mark_0
这个条件;
因此, @Mark_0
要改为: if( _cur - BB < 0 || Cont[ _cur - BB] == 0)
; 这个逻辑一定要搞清楚;