[1674] 使数组互补的最少操作次数
给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。
如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。
返回使数组 互补 的 最少 操作次数。
n == nums.length
2 <= n <= 105
1 <= nums[i] <= limit <= 105
n
是偶数。先转化成区间覆盖问题,再用相关数据结构去解决。
通过上面的解析,我把题目先往区间覆盖问题进行一下转化
有q个区间,每个区间有一个weight属性,求被n个区间覆盖到,并且weihgt总和最小的点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKnFatlu-1607084721711)
这种问题在我所掌握的方法中,可以通过线段树数据结构去解决,总复杂度是 O(nlogn)。
type node struct {
l, r int // 代表树结点代表的区间范围
leftChild, rightChild *node
delay int // 延迟标记
totalCount, totalWeight int // 总覆盖数,及总操作数
}
type SegmentTree struct {
nodes []node
root int
}
// 初始化线段树,分配内存大小, 构造树型
func (tree *SegmentTree) Init(l, r int) {
}
// 构造树型
func (tree *SegmentTree) buildNode(l, r, root int) *node {
return nil
}
func (tree *SegmentTree) InsertSegment(l, r, weight int) {
}
func (tree *SegmentTree) insert(l, r, root int) {
}
func (tree *SegmentTree) QueryPoint(x int) (totalCount, totalWeight int) {
return 0, 0
}
func (tree *SegmentTree) query(l, r, root int) *node {
return nil
}
func minMoves(nums []int, limit int) int {
segmentTree := &SegmentTree{}
segmentTree.Init(2, 2*limit) // 初始化线段树,范围是2- 2*limit
/* 构造线段添加到树中*/
n := len(nums)
for i := 0; i < n/2; i++ {
n1, n2 := getSort(nums[i], nums[n-1-i])
segmentTree.InsertSegment(n1+n2, n1+n2, 0) // 本身操作数为0
//- 1次操作能达到的区间就是 [num[i]+1,num[i]+num[n-1-i]), (num[i]+num[n-1-i], limit+num[n-1-i]]
segmentTree.InsertSegment(n1+1, n1+n2-1, 1)
segmentTree.InsertSegment(n1+n2+1, limit+n2, 1)
//- 2次操作能达到的区间就是 [2, num[i]+1), (limit+num[n-1-i], 2*limit]
segmentTree.InsertSegment(2, n1, 2)
segmentTree.InsertSegment(limit+n2+1, 2*limit, 2)
}
minOp := len(nums)
for x := 2; x <= 2*limit; x++ {
totCnt, opCnt := segmentTree.QueryPoint(x)
if totCnt == n/2 && opCnt < minOp {
minOp = opCnt
}
}
return minOp
}
type node struct {
l, r int // 代表树结点代表的区间范围
leftChild, rightChild *node
totalCount, totalWeight int // 总覆盖数,及总操作数
}
type SegmentTree struct {
nodes []node // 事先申请结点,加事内存分配
root int //根结点编号
}
// 初始化线段树,分配内存大小, 构造树型
func (tree *SegmentTree) Init(l, r int) {
tree.nodes = make([]node, (r-l+1)*4)
tree.root = 1 //
tree.buildNode(l, r, tree.root)
}
// 构造树型
func (tree *SegmentTree) buildNode(l, r, root int) *node {
if l > r {
return nil
}
mid := (l + r) >> 1
tree.nodes[root].l, tree.nodes[root].r = l, r
tree.nodes[root].totalCount, tree.nodes[root].totalWeight = 0, 0
if l == r {
return &tree.nodes[root]
}
// 构造左右子树
tree.nodes[root].leftChild = tree.buildNode(l, mid, root<<1)
tree.nodes[root].rightChild = tree.buildNode(mid+1, r, root<<1|1)
return &tree.nodes[root]
}
func (tree *SegmentTree) InsertSegment(l, r, weight int) {
tree.insert(l, r, weight, tree.root)
}
func (tree *SegmentTree) insert(l, r, weight, root int) {
if l > tree.nodes[root].r || r < tree.nodes[root].l {
return
}
if l <= tree.nodes[root].l && tree.nodes[root].r <= r {
tree.nodes[root].totalWeight += weight
tree.nodes[root].totalCount++
return
}
tree.insert(l, r, weight, root<<1)
tree.insert(l, r, weight, root<<1|1)
}
func (tree *SegmentTree) QueryPoint(x int) (totalCount, totalWeight int) {
n := tree.query(x, x, tree.root)
if n != nil {
return n.totalCount, n.totalWeight
}
return 0, 0
}
func (tree *SegmentTree) query(l, r, root int) *node {
if l > tree.nodes[root].r || r < tree.nodes[root].l {
return nil
}
if tree.nodes[root].l == tree.nodes[root].r {
return &tree.nodes[root]
}
tree.pushDown(root)
mid := (tree.nodes[root].l + tree.nodes[root].r) >> 1
if l <= mid {
return tree.query(l, r, root<<1)
}
return tree.query(l, r, root<<1|1)
}
func (tree *SegmentTree) pushDown(root int) {
totCnt, totWeight := tree.nodes[root].totalCount, tree.nodes[root].totalWeight
tree.nodes[root].totalCount, tree.nodes[root].totalWeight = 0, 0
tree.nodes[root<<1].totalCount += totCnt
tree.nodes[root<<1].totalWeight += totWeight
tree.nodes[root<<1|1].totalCount += totCnt
tree.nodes[root<<1|1].totalWeight += totWeight
}
func getSort(a, b int) (int, int) {
if a < b {
return a, b
}
return b, a
}
func minMoves(nums []int, limit int) int {
segmentTree := &SegmentTree{}
segmentTree.Init(2, 2*limit) // 初始化线段树,范围是2- 2*limit
/* 构造线段添加到树中*/
n := len(nums)
for i := 0; i < n/2; i++ {
n1, n2 := getSort(nums[i], nums[n-1-i])
segmentTree.InsertSegment(n1+n2, n1+n2, 0) // 本身操作数为0
//- 1次操作能达到的区间就是 [num[i]+1,num[i]+num[n-1-i]), (num[i]+num[n-1-i], limit+num[n-1-i]]
segmentTree.InsertSegment(n1+1, n1+n2-1, 1)
segmentTree.InsertSegment(n1+n2+1, limit+n2, 1)
//- 2次操作能达到的区间就是 [2, num[i]+1), (limit+num[n-1-i], 2*limit]
segmentTree.InsertSegment(2, n1, 2)
segmentTree.InsertSegment(limit+n2+1, 2*limit, 2)
}
minOp := len(nums)
for x := 2; x <= 2*limit; x++ {
totCnt, opCnt := segmentTree.QueryPoint(x)
if totCnt == n/2 && opCnt < minOp {
minOp = opCnt
}
}
return minOp
}