LeetCode 3695 - Maximize Alternating Sum Using Swaps

We are given an array nums and a list of allowed swap operations. The alternating sum of an array is defined as: Elements at even indices contribute positively, while elements at odd indices contribute negatively.

LeetCode Problem 3695

Difficulty: 🔴 Hard
Topics: Array, Greedy, Union-Find, Sorting

Solution

LeetCode 3695 - Maximize Alternating Sum Using Swaps

Problem Understanding

We are given an array nums and a list of allowed swap operations.

The alternating sum of an array is defined as:

$$nums[0] - nums[1] + nums[2] - nums[3] + \cdots$$

Elements at even indices contribute positively, while elements at odd indices contribute negatively.

The important detail is that the swaps are not one time operations. Every swap listed in swaps may be performed any number of times and in any order. Therefore, the question is not asking us to find a sequence of swaps. Instead, it asks us to determine the maximum alternating sum achievable using the connectivity induced by those swaps.

The array length can be as large as 10^5, and the number of swap pairs can also be as large as 10^5. This immediately rules out any solution that attempts to simulate permutations or search through swap sequences.

The key observation is that repeated swaps along connected indices allow arbitrary rearrangement of values within a connected component of the swap graph.

The input consists of:

  • nums[i], the value currently stored at index i
  • swaps[i] = [p, q], indicating indices p and q may be swapped

The output is the maximum possible alternating sum after performing any number of allowed swaps.

Several edge cases are important:

  • No swaps are available, so the original alternating sum must be returned.
  • All indices belong to one connected component, allowing complete rearrangement of the entire array.
  • Components may contain only even positions, only odd positions, or a mixture of both.
  • Values can be as large as 10^9, so 64 bit arithmetic is required.
  • Components of size one must still be processed correctly.

Approaches

Brute Force

A brute force solution would enumerate every arrangement reachable through the allowed swaps and compute the alternating sum of each arrangement.

To do this, we would first identify connected components, then generate every permutation of values within each component. Since a component of size k can generate k! permutations, the number of possible configurations grows explosively.

This approach is correct because it checks every reachable state and selects the best alternating sum. However, it becomes completely infeasible even for components of moderate size.

For example, a component containing just 15 indices already has over one trillion permutations.

Key Insight

Think of the swap pairs as edges in an undirected graph.

If two indices belong to the same connected component, repeated swaps allow us to move values along paths. Consequently, any value in that component can eventually be placed at any index within that component.

Therefore:

  • Values cannot move between different connected components.
  • Inside a component, values may be assigned arbitrarily to the component's indices.

Now consider the alternating sum.

Each index contributes either:

  • +nums[i] if the index is even
  • -nums[i] if the index is odd

For a connected component:

  • Let E be the number of even indices.
  • Let O be the number of odd indices.
  • We may assign any component value to any component index.

To maximize contribution:

  • Place the largest values into the positive slots.
  • Place the smallest values into the negative slots.

Thus, after sorting component values:

  • The largest E values should occupy even indices.
  • The smallest O values should occupy odd indices.

This becomes an independent optimization problem for each connected component.

To find components efficiently, we use Union-Find (Disjoint Set Union).

Approach Comparison

Approach Time Complexity Space Complexity Notes
Brute Force O(∏ k!) O(k) Enumerates all reachable permutations
Optimal O(n log n) O(n) DSU + sort values inside each component

Algorithm Walkthrough

  1. Create a Union-Find structure containing all indices.
  2. For every swap pair [u, v], union the two indices. After processing all edges, indices belonging to the same connected component will have the same representative.
  3. Traverse all indices and group them by component root.
  4. For each component:
  • Collect all values belonging to that component.
  • Count how many indices in the component are even.
  • Count how many indices in the component are odd.
  1. Sort the component values in ascending order.
  2. Since even positions contribute positively, assign the largest values to those positions.

If the component contains evenCount even indices:

  • The largest evenCount values contribute positively.
  • The remaining values contribute negatively.
  1. Compute the component contribution:
  • Add the largest evenCount values.
  • Subtract the remaining values.
  1. Sum the contributions from all components.
  2. Return the final answer.

Why it works

Within a connected component, every value can be assigned to any index. The alternating sum assigns coefficient +1 to even positions and -1 to odd positions.

The rearrangement inequality tells us that to maximize a weighted sum with coefficients consisting only of +1 and -1, the largest values should receive the positive coefficients and the smallest values should receive the negative coefficients.

Since components are independent and values cannot cross component boundaries, maximizing each component independently yields the globally optimal solution.

Python Solution

from typing import List
from collections import defaultdict

class Solution:
    def maxAlternatingSum(self, nums: List[int], swaps: List[List[int]]) -> int:
        n = len(nums)

        parent = list(range(n))
        size = [1] * n

        def find(x: int) -> int:
            while parent[x] != x:
                parent[x] = parent[parent[x]]
                x = parent[x]
            return x

        def union(a: int, b: int) -> None:
            ra = find(a)
            rb = find(b)

            if ra == rb:
                return

            if size[ra] < size[rb]:
                ra, rb = rb, ra

            parent[rb] = ra
            size[ra] += size[rb]

        for u, v in swaps:
            union(u, v)

        components = defaultdict(list)

        for i in range(n):
            components[find(i)].append(i)

        answer = 0

        for indices in components.values():
            values = [nums[i] for i in indices]
            values.sort()

            even_count = sum(1 for i in indices if i % 2 == 0)

            m = len(values)

            answer += sum(values[m - even_count:])
            answer -= sum(values[:m - even_count])

        return answer

The implementation begins by building a Union-Find structure. Every swap edge merges two indices into the same connected component.

After all unions are processed, indices are grouped according to their representative root.

For each component, we collect the values currently stored at those indices and sort them. We also count how many even positions exist inside the component.

Since the largest values should occupy positive slots, the sorted array naturally splits into two parts:

  • The largest even_count values contribute positively.
  • The remaining values contribute negatively.

Their contribution is added to the final answer.

Because every component is optimized independently, the accumulated result is globally optimal.

Go Solution

package main

import "sort"

func maxAlternatingSum(nums []int, swaps [][]int) int64 {
	n := len(nums)

	parent := make([]int, n)
	size := make([]int, n)

	for i := 0; i < n; i++ {
		parent[i] = i
		size[i] = 1
	}

	var find func(int) int
	find = func(x int) int {
		if parent[x] != x {
			parent[x] = find(parent[x])
		}
		return parent[x]
	}

	union := func(a, b int) {
		ra := find(a)
		rb := find(b)

		if ra == rb {
			return
		}

		if size[ra] < size[rb] {
			ra, rb = rb, ra
		}

		parent[rb] = ra
		size[ra] += size[rb]
	}

	for _, edge := range swaps {
		union(edge[0], edge[1])
	}

	components := make(map[int][]int)

	for i := 0; i < n; i++ {
		root := find(i)
		components[root] = append(components[root], i)
	}

	var answer int64 = 0

	for _, indices := range components {
		values := make([]int, len(indices))

		evenCount := 0

		for i, idx := range indices {
			values[i] = nums[idx]
			if idx%2 == 0 {
				evenCount++
			}
		}

		sort.Ints(values)

		m := len(values)

		for i := 0; i < m-evenCount; i++ {
			answer -= int64(values[i])
		}

		for i := m - evenCount; i < m; i++ {
			answer += int64(values[i])
		}
	}

	return answer
}

The Go solution follows exactly the same logic. The primary difference is that the return type is int64, which is necessary because the result can exceed the range of a 32 bit integer. Path compression and union-by-size are used to keep Union-Find operations effectively constant time.

Worked Examples

Example 1

nums = [1,2,3]
swaps = [[0,2],[1,2]]

Union operations:

Edge Result
(0,2) {0,2}
(1,2) {0,1,2}

Single component:

Indices Values
[0,1,2] [1,2,3]

Even positions inside component:

Index Sign
0 +
1 -
2 +

evenCount = 2

Sorted values:

[1,2,3]

Largest two values:

[2,3]

Smallest remaining value:

[1]

Contribution:

(2 + 3) - 1 = 4

Answer:

4

Example 2

nums = [1,2,3]
swaps = [[1,2]]

Components:

Component Indices Values
A [0] [1]
B [1,2] [2,3]

Component A:

evenCount = 1
contribution = +1

Component B:

Sorted values:

[2,3]

Even indices in component:

index 2 only
evenCount = 1

Contribution:

+3 - 2 = 1

Total:

1 + 1 = 2

Answer:

2

Example 3

nums = [1,1000000000,1,1000000000,1,1000000000]
swaps = []

Each index forms its own component.

Contribution:

Index Value Sign Contribution
0 1 + 1
1 1000000000 - -1000000000
2 1 + 1
3 1000000000 - -1000000000
4 1 + 1
5 1000000000 - -1000000000

Total:

3 - 3000000000
= -2999999997

Answer:

-2999999997

Complexity Analysis

Measure Complexity Explanation
Time O(n log n) Sorting values inside components dominates the running time
Space O(n) Union-Find arrays and component storage

The Union-Find operations contribute only O((n + m) α(n)), where α(n) is the inverse Ackermann function. The dominant cost comes from sorting component values. Since the total size of all components is n, the combined sorting cost is bounded by O(n log n).

Test Cases

# Example 1
assert Solution().maxAlternatingSum(
    [1, 2, 3],
    [[0, 2], [1, 2]]
) == 4

# Example 2
assert Solution().maxAlternatingSum(
    [1, 2, 3],
    [[1, 2]]
) == 2

# Example 3
assert Solution().maxAlternatingSum(
    [1, 1000000000, 1, 1000000000, 1, 1000000000],
    []
) == -2999999997

# Entire array connected
assert Solution().maxAlternatingSum(
    [1, 2, 3, 4],
    [[0, 1], [1, 2], [2, 3]]
) == 4  # (4+3)-(1+2)

# No swaps available
assert Solution().maxAlternatingSum(
    [5, 1],
    []
) == 4

# Single component of size two
assert Solution().maxAlternatingSum(
    [1, 10],
    [[0, 1]]
) == 9

# Multiple disconnected components
assert Solution().maxAlternatingSum(
    [4, 7, 1, 8],
    [[0, 1], [2, 3]]
) == 10

# Values already optimal
assert Solution().maxAlternatingSum(
    [10, 1, 9, 2],
    [[0, 2], [1, 3]]
) == 16

# Large values requiring 64-bit arithmetic
assert Solution().maxAlternatingSum(
    [10**9, 10**9, 10**9, 10**9],
    [[0, 1], [1, 2], [2, 3]]
) == 0

# Component containing only odd positions
assert Solution().maxAlternatingSum(
    [5, 100, 7, 200],
    [[1, 3]]
) == -288

Test Summary

Test Why
Example 1 Fully connected graph
Example 2 Partial connectivity
Example 3 No swaps available
Entire array connected Global rearrangement possible
No swaps available Original alternating sum preserved
Single component of size two Smallest nontrivial component
Multiple disconnected components Independent optimization
Values already optimal No beneficial rearrangement needed
Large values Verifies 64 bit arithmetic
Component containing only odd positions Handles all negative coefficient positions

Edge Cases

No Swap Operations

When swaps is empty, every index forms a singleton component. No values can move. A buggy implementation might still attempt to rearrange values globally. Our solution correctly treats every index as its own component, reproducing the original alternating sum exactly.

Entire Array Forms One Connected Component

If all indices become connected, every value can be moved to every position. This is the most powerful case and effectively becomes a global optimization problem. The algorithm naturally handles it because there is exactly one component, and all values are sorted together before assigning the largest values to positive positions.

Components Containing Only Odd or Only Even Positions

A component may contain indices with only one sign type. For example, a component could contain only odd indices, meaning every value contributes negatively. A common mistake is assuming both positive and negative slots exist. The formula remains valid because evenCount may be zero or equal to the component size. The split of the sorted values still produces the correct contribution.

Very Large Numbers

Values can be as large as 10^9, and there can be 10^5 of them. The final answer can therefore exceed the range of a 32 bit integer. The Go implementation uses int64, and Python integers automatically support arbitrary precision, ensuring correctness for all valid inputs.