二进制串“翻转”成全零所需的操作次数之分析

问题来源

洛谷P6366

问题的数学描述

0-9以及A-F为表示字符,输入一个字符串S SS,代表一个十六进制数,其对应的二进制数的位串为S B S_BSB,长度为n nn(去除前导零)。
定义操作L ( i ) L(i)L(i):将S B S_BSB的第i ii1、第( i − 1 ) (i-1)(i1)位、第( i + 1 ) (i+1)(i+1)位取反,若其中某位不存在则忽略(即i − 1 < 0 ∨ i + 1 > n − 1 i-1<0\lor i+1>n-1i1<0i+1>n1)。
设计程序,求解最少进行多少次操作L ( i ) L(i)L(i),能够使得S B S_BSB转换为全0串。

问题分析

将输入的字符串S SS转换为S B S_BSB并不困难,因为每个十六进制位(digit)唯一对应一组4-bit二进制串。主要的问题在于操作L LL

这个问题的目标是要进行操作,使得∀ i ∈ [ 0 , n ) ( i ∈ Z ) , S B [ i ] = 0 \forall i\in [0,n)(i\in \mathbb{Z}), S_B[i]=0i[0,n)(iZ),SB[i]=0。其实对于特定的位置i ii,我们只有两种选择,即进行或不进行L ( i ) L(i)L(i),因为在同一i ii处,不论进行多少次操作,其效果都相当于进行0次或1次操作。

涉及到按位取反,可以想到在布尔代数中A ˉ = A ⊕ 1 \bar A=A\oplus 1Aˉ=A12。所以,如果将操作L ( i ) L(i)L(i)用包含异或运算的伪代码来表示的话,它可以变成这样:
procedure L ( S B , i ) L(S_B,i)L(SB,i)
S B [ i − 1 ] : = S B [ i − 1 ] ⊕ 1 S_B[i-1]:=S_B[i-1]\oplus 1SB[i1]:=SB[i1]1
S B [ i ] : = S B [ i ] ⊕ 1 S_B[i]:=S_B[i]\oplus 1SB[i]:=SB[i]1
S B [ i + 1 ] : = S B [ i ] ⊕ 1 S_B[i+1]:=S_B[i]\oplus 1SB[i+1]:=SB[i]1
end procedure

如果操作L LL对同一位置取反了k kk次(k ≥ 0 k\geq 0k0),则该位置的值会变为b orig ⊕ ( 1 ⊕ 1 ⊕ ⋯ ⊕ 1 ⏟ k 个 1 ) = { b ˉ orig , k  is odd , b orig , k  is even . b_{\text{orig}}\oplus \left(\underbrace{1\oplus 1\oplus\cdots\oplus 1}_{k个1}\right)=\begin{cases} \bar b_\text{orig},&k\text{ is odd},\\b_\text{orig},&k\text{ is even}. \end{cases}borigk1111={bˉorig,borig,k is odd,k is even.

我们可以定义一个长度为n nn的二进制串p pp,某一位为1表示在该处应当进行L ( i ) L(i)L(i)(或进行奇数次),为0则表示在该处不应当进行L ( i ) L(i)L(i)(或进行偶数次)。在按照p pp进行完操作之后,为了满足全零的条件,由上面的公式可知{ p [ i − 1 ] ⊕ p [ i ] ⊕ p [ i + 1 ] = S B [ i ] , 1 ≤ i < n − 1 , p [ 0 ] ⊕ p [ 1 ] = S B [ 0 ] , ( ∗ ) p [ n − 2 ] ⊕ p [ n − 1 ] = S B [ n − 1 ] . \begin{cases} p[i-1]\oplus p[i]\oplus p[i+1]=S_B[i],&1\leq i<n-1,\\ p[0]\oplus p[1]=S_B[0],(*)\\ p[n-2]\oplus p[n-1]=S_B[n-1]. \end{cases}p[i1]p[i]p[i+1]=SB[i],p[0]p[1]=SB[0],()p[n2]p[n1]=SB[n1].1i<n1,
所以,解决这道题的目标就变成了寻找满足上述等式的p pp。如何找到它呢?我们可以从边界上入手,因为这些地方影响因素只有两个,相比于既要考虑左侧又要考虑右侧的中间位置显然简单得多。根据题目条件,前导零不会在S B S_BSB中存在,所以S B [ 0 ] = 1 S_B[0]=1SB[0]=1,我们就从这里入手。

此时,等式( ∗ ) (*)()便转化为p [ 0 ] ⊕ p [ 1 ] = 1 p[0]\oplus p[1]=1p[0]p[1]=1。根据异或运算的性质,p [ 0 ] = ( p [ 1 ] ) ′ p[0]=\left(p[1]\right)'p[0]=(p[1]),因此,前两位的组合要么是( 0 , 1 ) (0,1)(0,1),要么是( 1 , 0 ) (1,0)(1,0),只有这两种情况,我们可以逐个尝试。在已知前两位的情况下,我们我们可以继续应用第一个等式,将p pp的剩余部分求出。比如,当i = 1 i=1i=1时,p [ 0 ] ⊕ p [ 1 ] ⊕ p [ 2 ] = S B [ 1 ] p[0]\oplus p[1]\oplus p[2]=S_B[1]p[0]p[1]p[2]=SB[1],该式中已经只有一个未知数p [ 2 ] p[2]p[2]
在接近末尾的时候,再根据第三个等式判断当前的p pp是否能够满足要求,如果满足,那么目的就达到了。最终的答案就是两种情况下能够满足要求的p pp中1的个数的最小值。

代码?

没有代码。


  1. 首位为0 ↩︎

  2. ⊕ \oplus”表示异或运算 ↩︎


版权声明:本文为hyj53原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。