题解 | #牛牛打怪兽#
牛牛打怪兽
http://www.nowcoder.com/practice/a3b055dd672245a3a6e2f759c237e449
题意整理:
题目给出一个正数数组,每次操作可以使数组的第x,2x,2x+1三个元素的值同时减一,且只能够在三个元素都存在时才能够操作,求出将数组所有元素变为非正整数的最少操作次数。
首先,为了能够满足所有元素都能够被消除,数组长度必须为奇数,否则,如果数组长度为偶数,那么对数组最后一个元素,显然其序号为2的倍数(从1开始计数),设其为2x,那么需要对x,2x,2x+1进行操作才有可能对其进行消除,但数组不存在2x+1的元素,导出矛盾,故数组长度必须为奇数。
其次,数组长度必须不小于三,这是非常明显的,一次操作必定需要三个不同元素进行操作。
方法一:贪心+迭代
核心思想:
很容易想到一种解法,既采用贪心策略,从最中间的元素开始消除,因为消除此元素时会同时对数组最后的两个元素进行操作,且只有消除此元素才有可能对最后的元素进行操作,所以需要从中间开始往前操作,每次选出2x与2x+1中的较大者,操作对应次数保证其数字不为正,从中间开始不断往前消除就是最佳的策略,这保证了对最后元素(在不断变化)的操作次数是最小的。在操作完成后,如果首元素为正值,加上首元素的值即为总的操作次数。
核心代码:
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)。既for循环的开销
空间复杂度: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)。既递归开销,递归深度为n/2
空间复杂度:O(n),既递归使用的栈开销,大小为n/2