Skip to main content
Back to problems
#3086
Hard Algorithms

Minimum moves to pick k ones

Array Greedy Sliding Window Prefix Sum
21.5% acceptance
Feb 25, 2026
61
53
You are given a binary array nums of length n, a positive integer k and a non-negative integer maxChanges. Alice plays a game, where the goal is for Alice to pick up k ones from nums using the minimum number of moves. Alice picks up any index aliceIndex in the range [0, n - 1] and stands there. If nums[aliceIndex] == 1, Alice picks up the one and nums[aliceIndex] becomes 0 (this does not count as a move). After this, Alice can make any number of moves where in each move Alice must perform exactly one of the following actions: Select any index j != aliceIndex such that nums[j] == 0 and set nums[j] = 1. This action can be performed at most maxChanges times. Select any two adjacent indices x and y (|x - y| == 1) such that nums[x] == 1, nums[y] == 0, then swap their values. If y == aliceIndex, Alice picks up the one after this move and nums[y] becomes 0. Return the minimum number of moves required by Alice to pick exactly k ones.

Solution

Rust
Time O(n²)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn minimum_moves(nums: Vec<i32>, k: i32, max_changes: i32) -> i64 {
    let n = nums.len();
    let k = k as i64;
    let max_changes = max_changes as i64;
    // Positions of 1s
    let ones: Vec<i64> = (0..n).filter(|&i| nums[i] == 1).map(|i| i as i64).collect();
    let total_ones = ones.len() as i64;
    // Prefix sum of ones positions
    let prefix: Vec<i64> = {
      let mut p = vec![0i64; ones.len() + 1];
      for i in 0..ones.len() { p[i+1] = p[i] + ones[i]; }
      p
    };
    let mut ans = i64::MAX;
    // Try each position as Alice's starting position
    // When standing at pos p with some 1s nearby and using maxChanges:
    // We can pick up at most min(3, k, maxChanges*2...) via changes quickly
    // This is O(n) with sliding window on ones positions
    // For each center position (from ones), try picking nearby ones with sliding window
    // Actually: for each potential alice position (from ones array), sliding window of k ones
    // But that's complex. Let me use the sliding window approach on the ones array.
    // For a window of 'need' ones centered around some alice position:
    // - Ones at distance 1 or 2 can be moved to alice in 1 or 2 moves
    // - For 'grabs' via maxChanges, each costs 2 moves (create + move to alice)
    // Approach: try every alice position = ones[i]
    // For each alice position, we can instantly pick up nearby ones (within 2 steps via adjacent swaps)
    // But this gets complex. Use standard sliding window approach.
    // Greedy: 
    // - First, take up to min(k, maxChanges*2) via changes (2 moves each, 1 if adjacent=1)
    //   Actually: if there's a 1 adjacent, pick it up (1 move). Else create then pick (2 moves).
    //   But we want to minimize. Let's count: up to min(3, k) "free" from standing position + neighbors.
    //   Then use maxChanges for remaining (2 each). Then use slide for the rest (median trick).
    // This problem is complex. Let me implement the O(n log n) solution with binary search on window.
    
    for i in 0..ones.len() {
      // Alice at ones[i]
      let pos = ones[i];
      // Count immediately free picks (at pos, pos-1, pos+1)
      let free_picks = nums[pos as usize] as i64; // already picked in the problem (not a move)
      // Actually the "free" pick at aliceIndex is handled separately.
      // Use the ones array directly.
      // For each window of size k around i, compute cost.
      // Actually I'll use a different approach: try all windows of ones with sliding window.
      let _ = (free_picks, pos);
      break; // This approach is too complex, use a different method below
    }
    // Use the approach: for each alice position (which must be an optimal position, i.e., a median),
    // Binary search on window of ones of size 'window_size' and compute median cost.
    let m = ones.len();
    // For window of size w of ones, alice position at ones[mid], cost = sum |ones[j] - ones[mid]|
    // But we also have maxChanges (each change: 2 moves if no adjacent 1, 1 if adjacent 1)
    // Simplified: pretend max_changes are each 2 moves, and nearby ones slide at 1 move each.
    // Let need = k (ones to pick), changes = min(need, max_changes) via 2 moves each
    // remaining = need - changes from the ones array (slide)
    // But this isn't exact. Let me implement the standard approach:
    // Phase 1: use maxChanges changes (each 2 moves = place 1 adjacent then slide to alice)
    // Phase 2: pick remaining ones by sliding (window of remaining around alice, median optimal)
    // We want to minimize: 2 * used_changes + sum of distances to alice for slid ones
    // Try all splits: used_changes in [max(0, k - total_ones) .. min(k, max_changes)]
    
    let min_changes = (k - total_ones).max(0);
    let max_used = k.min(max_changes);
    
    if min_changes > max_used { return -1; } // impossible (shouldn't happen per constraints)
    
    // For each used_changes, need to slide (k - used_changes) ones
    // These ones should be the closest to alice position (median trick)
    
    for used_changes in min_changes..=max_used {
      let need_slide = k - used_changes;
      let cost_changes = used_changes * 2;
      if need_slide == 0 {
        ans = ans.min(cost_changes);
        continue;
      }
      let ns = need_slide as usize;
      if ns > m { continue; }
      // Sliding window of size ns over ones
      for r in (ns-1)..m {
        let l = r + 1 - ns;
        let mid = (l + r) / 2;
        let alice = ones[mid];
        // Sum of distances to alice for ones[l..=r]
        let left_sum = alice * (mid - l) as i64 - (prefix[mid] - prefix[l]);
        let right_sum = (prefix[r+1] - prefix[mid+1]) - alice * (r - mid) as i64;
        let dist_cost = left_sum + right_sum;
        // Count ones adjacent to alice (ones that can be picked up in 1 move instead of computing dist)
        // Actually if they're in the array, the dist is correct.
        // But wait: adjacent ones (at alice ± 1) have cost 1, not their actual distance.
        // The actual dist IS their distance. So this is fine.
        ans = ans.min(cost_changes + dist_cost);
      }
    }
    ans
  }
}