Binary search

· · 题解

看到这题第一反应可能是贪心,二分时每次使区间最小,但是这样做是不正确的。因为直接贪心时靠前的操作权值^*小,靠后的操作权值大,靠前的决策会影响靠后的决策,即权值小的决策影响权值大的决策,显然不正确。

例如对于 $n=13$,查询序列的第 $5$ 个(下标为 $4$,接下来的位置均指下标)。直接贪心过程为: $$ [0,12] \stackrel{w=1}{\longrightarrow} [0,5] \stackrel{w=0}{\longrightarrow} [3,5] \stackrel{w=1}{\longrightarrow} [4,5] \stackrel{w=0}{\longrightarrow} [4,4] $$ 但实际上有更优解法: $$ [0,12] \stackrel{w=0}{\longrightarrow} [0,6]\stackrel{w=0}{\longrightarrow}[4,6] \stackrel{w=1}{\longrightarrow} [4,4] $$ 第一步贪心看似比最优解短了 $1$ ,贪心的下一步使得查找的值出现在了中部,导致 $ [3,5] \stackrel{w=1}{\longrightarrow} [4,5]$ 这一步无法使得长度折半。而贪心前面短的那一步最优解在下一步除二时除掉了,而在更靠后的 $[4,6] \stackrel{w=1}{\longrightarrow} [4,4]$ 比贪心短了 $1$。 对于 $\text{sub2}$ ,发现每次 $w$ 的值不会影响答案,直接输出 $\log_2 n$ 即可。 发现询问次数只有 $100$ 次,直接暴力。为便于实现,可以采用递归实现的二分,每次递归时分两类递归取最小即可。 由于递归层数为 $\log_2 n$ 层,每层分两种情况,最终复杂度为 $O(nq)$,可以通过此题。 核心代码: ``` int find(int k,int f,int l) { if(f==l) return 0; int mid=(f+l)>>1,ret=32; if(mid<k) ret=find(k,mid+1,l); else ret=find(k,f,mid); mid=(f+l+1)>>1; if(mid<=k) ret=min(ret,find(k,mid,l)); else ret=min(ret,find(k,f,mid-1)); return ret+1; } int main() { int i,n,q,k; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",num+i); scanf("%d",&q); while(q--) { scanf("%d",&k); for(i=0;i<n;i++) if(num[i]==k) break; printf("%d\n",find(i,0,n-1)); } return 0; } ```