Easy

题目描述

给你一个整数数组 nums

首先选择一个起始位置 curr,使得 nums[curr] == 0,并选择一个移动方向(向左或向右)。

之后,重复以下过程:

  • 如果 curr 超出了范围 [0, n - 1],则过程结束。
  • 如果 nums[curr] == 0,则朝当前方向移动,即如果向右移动则 curr 递增,如果向左移动则 curr 递减。
  • 否则如果 nums[curr] > 0
    • nums[curr] 减 1。
    • 反转移动方向(左变右,右变左)。
    • 朝新方向移动一步。

如果在过程结束时数组 nums 中的每个元素都变为 0,则认为初始位置 curr 和移动方向的选择是有效的。

返回可能的有效选择数量。

示例 1:

输入:nums = [1,0,2,0,3]
输出:2

示例 2:

输入:nums = [2,3,4,0,4,1,0]
输出:0

约束条件:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100
  • 至少存在一个元素 i 使得 nums[i] == 0

解题思路

解题思路

这是一个模拟题,需要按照题目描述的规则来模拟整个过程。

核心思路:

  1. 找到所有值为 0 的位置作为可能的起始点
  2. 对每个起始点,尝试向左和向右两个方向
  3. 模拟移动过程:
    • 遇到 0 则继续当前方向移动
    • 遇到正数则减 1,反转方向,然后移动
    • 超出边界则结束
  4. 检查最终是否所有元素都变为 0

模拟过程的细节:

  • 当前位置为 0 时,直接按当前方向移动到下一位置
  • 当前位置为正数时,先减 1,再反转方向,最后移动
  • 需要避免无限循环,可以设置最大步数限制

时间复杂度优化: 由于数组长度最大 100,每个元素最大 100,理论上最多需要 100 × 100 = 10,000 步就能结束,可以安全地进行暴力模拟。

这道题的关键是正确理解题意并准确实现模拟逻辑,特别要注意方向反转的时机。

代码实现

class Solution {
public:
    int countValidSelections(vector<int>& nums) {
        int n = nums.size();
        int validCount = 0;
        
        // 尝试每个值为0的位置作为起始点
        for (int start = 0; start < n; start++) {
            if (nums[start] != 0) continue;
            
            // 尝试向左和向右两个方向
            for (int direction : {-1, 1}) {
                vector<int> temp = nums;
                int curr = start;
                int dir = direction;
                bool valid = true;
                int steps = 0;
                const int MAX_STEPS = 10000;
                
                while (steps < MAX_STEPS) {
                    steps++;
                    
                    if (curr < 0 || curr >= n) {
                        break;
                    }
                    
                    if (temp[curr] == 0) {
                        curr += dir;
                    } else {
                        temp[curr]--;
                        dir = -dir;
                        curr += dir;
                    }
                }
                
                // 检查是否所有元素都变为0
                bool allZero = true;
                for (int x : temp) {
                    if (x != 0) {
                        allZero = false;
                        break;
                    }
                }
                
                if (allZero) {
                    validCount++;
                }
            }
        }
        
        return validCount;
    }
};
class Solution:
    def countValidSelections(self, nums: List[int]) -> int:
        n = len(nums)
        valid_count = 0
        
        # 尝试每个值为0的位置作为起始点
        for start in range(n):
            if nums[start] != 0:
                continue
            
            # 尝试向左和向右两个方向
            for direction in [-1, 1]:
                temp = nums[:]
                curr = start
                dir = direction
                steps = 0
                MAX_STEPS = 10000
                
                while steps < MAX_STEPS:
                    steps += 1
                    
                    if curr < 0 or curr >= n:
                        break
                    
                    if temp[curr] == 0:
                        curr += dir
                    else:
                        temp[curr] -= 1
                        dir = -dir
                        curr += dir
                
                # 检查是否所有元素都变为0
                if all(x == 0 for x in temp):
                    valid_count += 1
        
        return valid_count
public class Solution {
    public int CountValidSelections(int[] nums) {
        int n = nums.Length;
        int validCount = 0;
        
        // 尝试每个值为0的位置作为起始点
        for (int start = 0; start < n; start++) {
            if (nums[start] != 0) continue;
            
            // 尝试向左和向右两个方向
            foreach (int direction in new int[] {-1, 1}) {
                int[] temp = new int[n];
                Array.Copy(nums, temp, n);
                int curr = start;
                int dir = direction;
                int steps = 0;
                const int MAX_STEPS = 10000;
                
                while (steps < MAX_STEPS) {
                    steps++;
                    
                    if (curr < 0 || curr >= n) {
                        break;
                    }
                    
                    if (temp[curr] == 0) {
                        curr += dir;
                    } else {
                        temp[curr]--;
                        dir = -dir;
                        curr += dir;
                    }
                }
                
                // 检查是否所有元素都变为0
                bool allZero = true;
                for (int i = 0; i < n; i++) {
                    if (temp[i] != 0) {
                        allZero = false;
                        break;
                    }
                }
                
                if (allZero) {
                    validCount++;
                }
            }
        }
        
        return validCount;
    }
}
/**
 * @param {number[]} nums
 * @return {number}
 */
var countValidSelections = function(nums) {
    let count = 0;
    
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] === 0) {
            // Try moving left
            if (simulate([...nums], i, -1)) count++;
            // Try moving right
            if (simulate([...nums], i, 1)) count++;
        }
    }
    
    return count;
};

function simulate(nums, curr, direction) {
    while (curr >= 0 && curr < nums.length) {
        if (nums[curr] === 0) {
            curr += direction;
        } else {
            nums[curr]--;
            direction *= -1;
            curr += direction;
        }
    }
    
    return nums.every(num => num === 0);
}

复杂度分析

复杂度类型复杂度
时间复杂度O(n × S)
空间复杂度O(n)

其中 n 是数组长度,S 是模拟步数的上界。在最坏情况下,S ≈ n × max(nums[i]),由于约束条件 n ≤ 100,nums[i] ≤ 100,所以 S ≤ 10,000,实际时间复杂度约为 O(n²)。