贪心4
贪心算法通过每步选择局部最优解,期望最终达到全局最优,适用于最优化和排序问题。
贪心4
贪心4
1675. 数组的最小偏移量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <vector>
#include <set>
using namespace std;
class Solution {
public:
// 返回数组执行某些操作后可以拥有的最小偏移量
int minimumDeviation(vector<int> &nums) {
// 使用 multiset 维护有序集合
multiset<int> s;
// 初始化:将所有元素变为偶数插入 multiset 中
for (int num: nums) {
if (num % 2 == 0) {
s.insert(num); // 偶数直接插入
} else {
s.insert(num * 2); // 奇数先乘2变为偶数再插入
}
}
int res = *s.rbegin() - *s.begin(); // 当前最大偏移量
// 尽可能减小最大值(最大值为偶数时可以继续除2)
while (res > 0 && *s.rbegin() % 2 == 0) {
int maxVal = *s.rbegin(); // 当前最大值
s.erase(prev(s.end())); // 删除最大值
s.insert(maxVal / 2); // 插入最大值的一半
res = min(res, *s.rbegin() - *s.begin()); // 更新最小偏移量
}
return res;
}
};
781. 森林中的兔子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// 根据兔子的回答,计算森林中最少有多少只兔子
int numRabbits(vector<int> &arr) {
// 先排序,让相同回答的兔子聚集在一起
sort(arr.begin(), arr.end());
int n = arr.size();
int res = 0;
// 遍历排序后的数组
for (int i = 0, j = 1, x; i < n; j++) {
x = arr[i];
// 扩展到所有连续相同回答的兔子
while (j < n && arr[j] == x) j++;
// i 到 j - 1 是回答为 x 的兔子,共有 j - i 个
// 每组最多可以有 x + 1 只兔子(包括自己和回答中提到的)
// 把这连续的 j - i 个进行分组,每组的兔子数为 x + 1,凑不成一组的也按照一组算,即 (j - i) / (x + 1) 向上取整
// a / b 向上取整:(a + b - 1) / b
// (j - i) / (x + 1) 向上取整:(j - i + x + 1 - 1) / (x + 1) = (j - i + x) / (x + 1)
// 需要 (j - i + x) / (x + 1) 组,每组 x + 1 只
res += ((j - i + x) / (x + 1)) * (x + 1);
i = j; // 进入下一组的起始位置
}
return res;
}
};
2449. 使数组相似的最少操作次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
// 将数组 nums 变得与 target 相似的最少操作次数
// nums[i] + 2, nums[j] - 2 操作可以进行任意次,要求最终元素频率相同
long long makeSimilar(vector<int> &nums, vector<int> &target) {
int n = nums.size();
int oddSize = split(nums);
split(target);
// 奇偶分别排序
sort(nums.begin(), nums.begin() + oddSize); // 奇数部分
sort(nums.begin() + oddSize, nums.end()); // 偶数部分
sort(target.begin(), target.begin() + oddSize);
sort(target.begin() + oddSize, target.end());
long long res = 0;
for (int i = 0; i < n; i++)
res += abs((long long) nums[i] - target[i]);
// 每次操作会让差值减少4(+2 -2),所以结果除以4
return res / 4;
}
// 将数组分为奇数和偶数部分(奇数排在前面),返回奇数部分长度
int split(vector<int> &arr) {
int oddSize = 0;
for (int i = 0; i < arr.size(); i++)
if (arr[i] % 2 == 1)
swap(arr[i], arr[oddSize++]);
return oddSize;
}
};
知识竞赛
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 200001;
int n;
pair<int, int> nums[MAXN];
// 计算最大 min((ai+aj)/2, (bi+bj)/2) * 2 的值
int compute() {
// 根据 abs(ai - bi) 从小到大排序
sort(nums, nums + n, [](const pair<int, int> &a, const pair<int, int> &b) {
return abs(a.first - a.second) < abs(b.first - b.second);
});
int maxA = nums[0].first;
int maxB = nums[0].second;
int res = 0;
for (int i = 1; i < n; ++i) {
if (nums[i].first <= nums[i].second) {
res = max(res, maxA + nums[i].first);
} else {
res = max(res, maxB + nums[i].second);
}
maxA = max(maxA, nums[i].first);
maxB = max(maxB, nums[i].second);
}
return res;
}
int main() {
while (cin >> n) {
for (int i = 0; i < n; ++i) {
cin >> nums[i].first >> nums[i].second;
}
int res = compute();
cout << fixed;
cout.precision(1);
cout << (double) res / 2 << '\n';
}
return 0;
}
将数组分成几个递增序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <vector>
#include <algorithm>
// 将数组分成几个递增序列
// 给你一个有序的正数数组 nums 和整数 K
// 判断该数组是否可以被分成一个或几个长度至少为 K 的不相交的递增子序列
// 数组中的所有数字,都要被若干不相交的递增子序列包含
using namespace std;
// 判断有序数组能否分成若干长度至少为k的递增子序列
bool canDivideIntoSubsequences(vector<int> &nums, int k) {
int n = nums.size();
int cnt = 1;
// 最大词频
int maxCnt = 1;
for (int i = 1; i < n; ++i) {
if (nums[i] != nums[i - 1]) {
maxCnt = max(maxCnt, cnt);
cnt = 1;
} else {
cnt++;
}
}
maxCnt = max(maxCnt, cnt);
return n / maxCnt >= k;
}
/*
* 分成 maxCnt 个数组 vector<vector<int>> arr(maxCnt),先把最大词频的单词一组分一个
* 然后最大词频左边的所有数字,按照 arr 下标大小 0,1,2...maxCnt-1,0,1... 一组一个
* 最大词频右边的所有数字,按照下标逆序一组一个,maxCnt-1,maxCnt-2...2,1,0,maxCnt-1...
* 使得所有数字尽量平均给每个数组
*/
int main() {
// 示例:nums = [1,2,2,3,3,3], k = 2
vector<int> nums = {1, 2, 2, 3, 3, 3};
int k = 2;
if (canDivideIntoSubsequences(nums, k)) {
cout << "true\n";
} else {
cout << "false\n";
}
return 0;
}
871. 最低加油次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
class Solution {
public:
// 计算从起点出发到达目标位置所需的最少加油次数
int minRefuelStops(int target, int startFuel, vector<vector<int>> &stations) {
// 如果起始油量已经足够到达终点,直接返回 0
if (startFuel >= target) return 0;
// 大根堆:用于保存沿途经过的加油站的油量
priority_queue<int> maxHeap;
int to = startFuel; // 当前最远能达到的位置(初始油量)
int cnt = 0; // 加油次数
// 遍历每个加油站
for (auto &station: stations) {
int position = station[0]; // 加油站位置
int fuel = station[1]; // 加油站油量
// 如果当前油量无法到达下一个加油站,就从之前路过的加油站加油
while (!maxHeap.empty() && to < position) {
to += maxHeap.top(); // 选择之前油量最多的加油站加油
maxHeap.pop();
cnt++; // 加油次数 +1
// 如果已经够到终点,提前返回
if (to >= target) return cnt;
}
// 如果加完油还是到不了这个加油站,说明无法继续
if (to < position) return -1;
// 当前加油站可以作为未来的备选项,存入堆中
maxHeap.push(fuel);
}
// 所有加油站都过了,还没到终点,继续加油看能不能冲到终点
while (!maxHeap.empty()) {
to += maxHeap.top();
maxHeap.pop();
cnt++;
if (to >= target) return cnt;
}
// 到这里说明油量耗尽也到不了终点
return -1;
}
};
本文由作者按照 CC BY 4.0 进行授权