这里讲解一种general的方法,如果我们给定了一个数组arr,然后在可以相邻的可以进行交换的情况下,变成另一个给定的数组,最小操作次数是多少。
对于本题就是
我们可以按照的元素值对应的顺序,给
数组换成对应的索引值,问题就会转变为求逆序对的数量。逆序对可以分治或者树状数组求解,树状数组写比较简单,这里采用树状数组写法。
这里举两个例子:
对于样例1:
arr = 11100
target = 10101
target元素对应的索引 0:[1,3] 1:[0,2,4]
处理出arr对应的索引 [0,2,4,1,3] ,逆序对就为3,可以自己手算一下。
对于样例2:
arr = 01011
target = 10101
target元素对应的索引 0:[0,2] 1:[1,3,4]
处理出arr对应的索引 [1,0,3,2,4],逆序对就为2,可以自己手算一下。
code
import sys
from math import inf
# 输入加速
input = sys.stdin.readline
if __name__ == '__main__':
s = input().strip()
n = len(s)
"""树状数组模板"""
tr = [0] * (n + 1)
def add(i: int, x: int):
while i <= n:
tr[i] += x
i += i & -i
def query(i: int) -> int:
res = 0
while i:
res += tr[i]
i -= i & -i
return res
"""树状数组模板结束"""
# 处理出上面所说的arr对应的索引数组。
def process(t: str):
lst = [[],[]]
for i in range(n):
if t[i] == '0':
lst[0].append(i)
else:
lst[1].append(i)
idxs = []
j = k = 0
for i in range(n):
if s[i] == '0':
if j < len(lst[0]):
idxs.append(lst[0][j])
else:
break
j += 1
else:
if k < len(lst[1]):
idxs.append(lst[1][k])
else:
break
k += 1
if len(idxs) == n:
return idxs
else:
return []
idxs = process("10" * n)
res = inf
if idxs:
cnt = 0
tot = 0
for i in idxs:
cnt += tot - query(i + 1)
add(i + 1, 1)
tot += 1
res = min(res,cnt)
idxs = process("01" * n)
tr = [0] * (n + 10)
if idxs:
cnt = 0
tot = 0
for i in idxs:
cnt += tot - query(i + 1)
add(i + 1, 1)
tot += 1
res = min(res,cnt)
print(res)