-
什么是贪心算法
贪心策略:局部最优 -> 全局最优
“贪婪+鼠目寸光”
1、把解决问题的过程分为若干步
2、解决每一步的时候,都选择当前看起来“最优的”解法
3、“希望”得到全局最优解 -
贪心算法的特点
贪心策略的提出是没有标准或者模板的
正确的贪心策略的提出是要有数学依据的
- 柠檬水找零
class Solution {
unordered_map<int, int> change = {
{5, 0},
{10, 0},
{20, 0},
};
public:
bool lemonadeChange(vector<int>& bills) {
for (int e : bills) {
if (e == 5) {
change[5]++;
} else if (e == 10) {
if (change[5] >= 1) {
change[5]--;
change[10]++;
} else {
return false;
}
} else {
if (change[10] >= 1 && change[5] >= 1) {
change[5]--;
change[10]--;
change[20]++;
}
else if (change[5] >= 3) {
change[5] -= 3;
change[20]++;
} else {
return false;
}
}
}
return true;
}
};
- 将数组和减半的最少操作次数
class Solution {
double sum = 0;
double mid = 0;
int count = 0;
priority_queue<double> pq;
public:
int halveArray(vector<int>& nums) {
for (int e : nums) {
sum += e;
pq.push(e);
}
mid = sum / 2;
sum = 0;
while (sum < mid) {
double top = pq.top(); pq.pop();
top /= 2;
sum += top;
++count;
pq.push(top);
}
return count;
}
};
- 最大数
class Solution {
string ret;
vector<string> v;
public:
string largestNumber(vector<int>& nums) {
for (int e : nums) {
v.push_back(to_string(e));
}
ranges::sort(v, [](const string& left, const string& right){
return left + right > right + left;
});
if (v.front() == "0") return "0";
for (const string& s : v) {
ret += s;
}
return ret;
}
};
- 摆动序列
// 解法一
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int left = nums[0];
int count = 1;
int right;
bool flag;
int i = 1;
for (; i < nums.size();) {
if (nums[i] > left) {
right = nums[i];
++i;
while (i < nums.size() && nums[i] >= right) {
right = nums[i];
++i;
}
++count;
flag = true; // true 为上升
left = right;
break;
} else if (nums[i] < left) {
right = nums[i];
++i;
while (i < nums.size() && nums[i] <= right) {
right = nums[i];
++i;
}
++count;
flag = false; // false 为下降
left = right;
break;
} else {
++i;
}
}
for (; i < nums.size();) {
if (flag) { // 前一个是上升
if (nums[i] < left) {
right = nums[i];
++i;
while (i < nums.size() && nums[i] <= right) {
right = nums[i];
++i;
}
++count;
flag = !flag;
left = right;
} else {
++i;
}
} else {
if (nums[i] > left) {
right = nums[i];
++i;
while (i < nums.size() && nums[i] >= right) {
right = nums[i];
++i;
}
++count;
flag = !flag;
left = right;
} else {
++i;
}
}
}
return count;
}
};
// 解法二
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if (nums.size() < 2) return 1;
int ret = 0, left = 0;
for (int i = 1; i < nums.size(); ++i) {
int right = nums[i] - nums[i-1];
if (right == 0) continue;
if (left * right <= 0) ++ret;
left = right;
}
return ret + 1;
}
};
- 最长递增子序列
存什么:所有长度为 n 的递增子序列中,最后一个元素的最小值
存哪里:所有大于等于 nums[i] 的最小值的位置
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> v = { nums[0] };
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] < v.back()) {
int left = 0, right = v.size() - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (v[mid] < nums[i]) {
left = mid + 1;
} else {
right = mid;
}
}
v[left] = nums[i];
} else if (nums[i] > v.back()) {
v.push_back(nums[i]);
}
}
return v.size();
}
};
- 递增的三元子序列
bool increasingTriplet(vector<int>& nums) {
vector<int> tuple = { nums[0] };
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] > tuple.back()) {
tuple.push_back(nums[i]);
if (tuple.size() == 3) return true;
} else {
for (int j = 0; j < tuple.size(); ++j) {
if (nums[i] <= tuple[j]) {
tuple[j] = nums[i];
break;
}
}
}
}
return false;
}
// 简化
bool increasingTriplet(vector<int>& nums) {
int num1 = nums[0], num2 = INT_MAX;
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] > num2) return true;
else if (nums[i] <= num1) num1 = nums[i];
else num2 = nums[i];
}
return false;
}
- 最长连续递增序列
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int ret = 0;
int left = 0, right = 1;
while (right < nums.size()) {
int cur_len = 0;
while (right < nums.size() && nums[left++] < nums[right++]) {
++cur_len;
}
ret = max(ret, cur_len);
}
return ret + 1;
}
};
- 买卖股票的最佳时机
int maxProfit(vector<int>& prices) {
int ret = 0;
int prev_min = prices[0];
for (int i = 1; i < prices.size(); ++i) {
ret = max(ret, prices[i] - prev_min);
prev_min = min(prev_min, prices[i]);
}
return ret;
}
- 买卖股票的最佳时机 II
int maxProfit(vector<int>& prices) {
int ret = 0;
for (int i = 1; i < prices.size(); ++i) {
if (prices[i] > prices[i-1]) ret += (prices[i] - prices[i-1]);
}
return ret;
}
- K 次取反后最大化的数组和
class Solution {
int ret = 0;
int plus_min = INT_MAX;
vector<int> minus;
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
for (int e : nums) {
if (e < 0) minus.push_back(e);
else {
ret += e;
plus_min = min(plus_min, e);
}
}
if (k > minus.size()) {
for (int e : minus) {
ret += -e;
plus_min = min(plus_min, -e);
}
k -= minus.size();
if (k % 2) ret -= 2 * plus_min;
} else {
ranges::sort(minus);
for (int i = 0; i < k; ++i) ret += -minus[i];
for (int i = k; i < minus.size(); ++i) ret += minus[i];
}
return ret;
}
};