题意整理:

题目给出一个正数数组,每次操作可以使数组的第x,2x,2x+1三个元素的值同时减一,且只能够在三个元素都存在时才能够操作,求出将数组所有元素变为非正整数的最少操作次数。

首先,为了能够满足所有元素都能够被消除,数组长度必须为奇数,否则,如果数组长度为偶数,那么对数组最后一个元素,显然其序号为2的倍数(从1开始计数),设其为2x,那么需要对x,2x,2x+1进行操作才有可能对其进行消除,但数组不存在2x+1的元素,导出矛盾,故数组长度必须为奇数。

其次,数组长度必须不小于三,这是非常明显的,一次操作必定需要三个不同元素进行操作。

方法一:贪心+迭代

核心思想:

很容易想到一种解法,既采用贪心策略,从最中间的元素开始消除,因为消除此元素时会同时对数组最后的两个元素进行操作,且只有消除此元素才有可能对最后的元素进行操作,所以需要从中间开始往前操作,每次选出2x与2x+1中的较大者,操作对应次数保证其数字不为正,从中间开始不断往前消除就是最佳的策略,这保证了对最后元素(在不断变化)的操作次数是最小的。在操作完成后,如果首元素为正值,加上首元素的值即为总的操作次数。 alt

核心代码:

class Solution {
public:
    int slove(int n, vector<int>& A) {
        if(n < 3 || n % 2 == 0) return -1;//无法全部打死
        int ans = 0;
        for(int i = n / 2 - 1; i >= 0; --i) {
            //从中间开始
            int res = max(A[2 * i + 1], A[2 * i + 2]);//保证能够打完,因为后续不会再对其处理
            ans += res;
            A[i] -= min(res, A[i]);//A[i]对应减少血量
        }
        if(A[0] > 0) ans += A[0];//消除第1个
        return ans;
    }
};

复杂度分析:

时间复杂度:O(n)O(n)O(n)。既for循环的开销

空间复杂度:O(1)O(1)O(1),只使用了常数个变量

方法二:贪心+递归

核心思想:

方法二思路与方法一相同,不过采用递归替代原本的迭代。递归结束条件既当前位置越过首元素。

核心代码:

class Solution {
public:
    int ans = 0;
    void dfs(vector<int>& A, int p) {
        if(p < 0) {
            //对A[0]做处理
            ans += A[0];
            return;
        }
        //得到最少需要打的次数
        int res = max(A[2 * p + 1], A[2 * p + 2]);//保证能够打完,因为后续不会再对其处理
        ans += res;
        A[p] -= min(res, A[p]);
        dfs(A, p - 1);
    }
    int slove(int n, vector<int>& A) {
        if(n < 3 || n % 2 == 0) return -1;//无法全部打死
        dfs(A, n / 2 - 1);
        return ans;
    }
};

复杂度分析:

时间复杂度:O(n)O(n)O(n)。既递归开销,递归深度为n/2n/2n/2

空间复杂度:O(n)O(n)O(n),既递归使用的栈开销,大小为n/2n/2n/2