笔试算法之王大锤发奖金问题
需求
- 论资排辈发奖金: 按照工龄发奖金,任意两个相邻的员工之间,工龄高的发工资必须多。
- 每人至少发100。
- 发的奖金总数最少
思路一
- 先给每人算100
-
两次遍历:
- 第一次从左到右遍历,出现右边比左边工龄高的,给右边的多发100.
- 第二次从右往左遍历,出现左边比右边工龄高的,然而左边工资却不比右边高的话,给左边的多发100.
- 将奖金数组内的值累加,返回结果。
JS实现思路一
function calcBonus(peopleArr) {
var resultArr = [];
var n = peopleArr.length
for (let i = 0; i < n; i++) {
resultArr[i] = 100;
}
for (let i = 1; i < n; i++) {
if (peopleArr[i] > peopleArr[i - 1]) {
resultArr[i] = resultArr[i - 1] + 100;
}
}
for (let i = n - 1; i > -1; i--) {
// 判断左边的是否比右边工龄高,且工资不比右边的高
if (peopleArr[i - 1] > peopleArr[i] && resultArr[i - 1] < resultArr[i] + 100) {
resultArr[i - 1] = resultArr[i] + 100;
}
}
var result = 0;
for (let i = 0; i < n; i++) {
result += resultArr[i]
}
return result;
}
// 测试用例:
var peopleArr1 = [9, 6, 3]
calcBonus(peopleArr1) //300, 200, 100 => 600
var peopleArr2 = [1, 4, 5, 9, 3, 2]
calcBonus(peopleArr2)// 100, 200, 300, 400, 200, 100 => 1300
时间复杂度为O(N),空间复杂度为O(N)
思路二
只遍历一次,直接求出最终需要发的奖金和:
- 先给第一个人发100;
- 右边和左边的工龄相等,同样发100;
- 右边比左边的工龄高,追加100
-
右边的工龄比左边的低,那么需要深入讨论:
- 假设目前出现左边出现的工龄为最高,一直向后找到最后一个工龄不再降低的人为止。
- 然后向前推,每往前一个,就追加100,追加到第一个工龄开始降低的人,相当于等差数列。所以,假设连续降低了n个人,那么这些人的奖金一共为 100n * (n + 1) / 2
- 最后,还要判断最开始左边出现的工龄最高的人是否需要追加。如果他的奖金不大于右边的,那么就需要追加100n减去他的原奖金的再加上100.
为更好理解,可看以下两个测试用例:
var peopleArr3 = [1, 5, 4, 2, 3, 1] // 100, 300, 200, 100, 200, 100 => 1000
var peopleArr4 = [1, 2, 3, 4, 5, 3, 2, 1] // 100, 200, 300, 400, 500, 300, 200, 100 => 2100
PS: 为方便计算,可将单位值设为1,那么最后的result * 100即可。
JS实现思路二
function calcBonus2(peopleArr) {
var result = 1;
var n = peopleArr.length;
var curMax = 1;
var count = 0;
for(let i = 1; i < n; i++) {
if(peopleArr[i] >= peopleArr[i - 1]){
if(count){
accCount();
}
curMax = peopleArr[i] == peopleArr[i - 1] ? 1 : curMax + 1
result += curMax
}else {
++count
}
}
if(count) {
accCount();
}
function accCount() {
result += count * (count + 1) / 2;
if(count >= curMax){
result += count - curMax + 1;
count = 0;
curMax = 1;
}
}
return result * 100;
}
calcBonus2(peopleArr3); // 1000
calcBonus2(peopleArr4); // 2100
时间复杂度O(n),空间复杂度O(1)