贪心复习
- 区间问题
- 区间选点
- 最大不相交区间数量
- 区间覆盖
- 区间分组
- Huffman树
- 合并果子
- 排序不等式
- 排队打水
- 绝对值不等式
- 货仓选址
- 推公式
- 耍杂技的牛
本文主要部分参考了Acwing算法基础课,详情可以去看看。
区间问题
- 知识点:排序+贪心
- 思路:
- 将所有区间按照左端点从左到右排序
- 从右向左遍历所有区间,若当前区间不包含点,则选取当前区间左端点(因为这样能使更多区间包含该点,体现了贪心的思想)
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 110000;
PII segs[N];
int n;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)cin >> segs[i].first >> segs[i].second;
sort(segs, segs + n);
int ans = 0, last = 1e9 + 1;
for(int i = n - 1; i >= 0; i--)
{
int l = segs[i].first, r = segs[i].second;
if(r < last)
{
last = l;
ans++;
}
}
cout << ans;
return 0;
}
分析同上题
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 110000;
PII segs[N];
int n;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)cin >> segs[i].first >> segs[i].second;
sort(segs, segs + n);
int ans = 0, last = 1e9 + 1;
for(int i = n - 1; i >= 0; i--)
{
int l = segs[i].first, r = segs[i].second;
if(r < last)
{
last = l;
ans++;
}
}
cout << ans;
return 0;
}
- 知识点:贪心+排序+小根堆
- 思路:
- 将所有区间按照左端点从左到右排好序,从左到右遍历每个区间,若当前区间左端点大于所有分组的右端点的最小值,则将该区间加入到该组,否则创建一个新的分组将当前区间放进去。(分组的右端点指的是分组最右边区间的右端点)
- 用一个小根堆来维护所有分组的右端点
- 贪心体现在每次将当前区间加入分组都是加入的分组右端点最小的那个分组,这样使得每个分组的区间更加紧凑,能够容纳更多区间,从而使得分组数量更少。
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 110000;
PII segs[N];
int n;
priority_queue<int, vector<int>, greater<int> > heap;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)cin >> segs[i].first >> segs[i].second;
sort(segs, segs + n);
for(int i = 0; i < n; i++)
{
int l = segs[i].first, r = segs[i].second;
if(i == 0)
{
heap.push(r);
continue;
}
if(l > heap.top())
{
heap.pop();
heap.push(r);
}
else heap.push(r);
}
int ans = heap.size();
cout << ans;
return 0;
}
- 知识点:贪心
- 思路:
- 将所有区间按照左端点从左到右排序
- 每次都选取符合条件的区间的右端点最大(记为x)的一个区间(符合条件是指区间左端点小于等于待覆盖区间的左端点),然后更新待覆盖区间左端点为x
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 110000;
PII segs[N];
int n, s, t;
int main()
{
cin >> s >> t;
cin >> n;
for(int i = 0; i < n; i++)cin >> segs[i].first >> segs[i].second;
sort(segs, segs + n);
int ans = 0;
for(int i = 0; i < n; i++)
{
if(segs[i].first <= s)
{
int max_r = -1e9, j = i;
while(j < n && segs[j].first <= s)
{
max_r = max(max_r, segs[j].second);
j++;
}
ans++;
s = max_r;
if(s >= t)
{
cout << ans;
return 0;
}
}
else break;
}
cout << -1;
return 0;
}
Huffman树
- 知识点:贪心+小根堆
- 思路:每次将最小的两堆合并,用一个小根堆来维护所有堆果子的数量
#include<iostream>
#include<queue>
using namespace std;
int n;
priority_queue<int, vector<int>, greater<int> > heap;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)
{
int x;
cin >> x;
heap.push(x);
}
int ans = 0;
while(heap.size() > 1)
{
int a = heap.top(); heap.pop();
int b = heap.top(); heap.pop();
ans += a + b;
heap.push(a + b);
}
cout << ans;
return 0;
}
排序不等式
- 知识点:排序、贪心
- 思路:
- 等待时间越少的排在越前面越好
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 110000;
int n, a[N];
int main()
{
cin >> n;
for(int i = 0; i < n; i++)cin >> a[i];
sort(a, a + n);
long long ans = 0;
for(int i = 0; i < n; i++)ans += a[i] * (n - 1 - i);
cout << ans;
return 0;
}
绝对值不等式
- 知识点:贪心
- 思路:
- 排序后奇数个点放中间,偶数个点放中间两个点之间任意位置均可,为了方便就放中间两个点左边那个
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 110000;
int n, a[N];
int main()
{
cin >> n;
int mid = n / 2;
for(int i = 0; i < n; i++)cin >> a[i];
sort(a, a + n);
int ans = 0;
for(int i = 0; i < n; i++)ans += abs(a[i] - a[mid]);
cout << ans;
return 0;
}
推公式
- 知识点:排序、公式推导
- 思路:
- 交换序列任意两个相邻奶牛的位置对其他奶牛的风险值无影响,所以可以通过交换任意两个相邻奶牛的位置来观察风险值的最大值的可能情况。
奶牛 | 交换前 | 交换后 |
---|---|---|
i | (w1 + w2 + … + wi-1) - si | (w1 + w2 + … + wi-1) + wi+1 - si |
i + 1 | (w1 + w2 + … + wi-1) + wi - si+1 | (w1 + w2 + … + wi-1) - si+1 |
同时减去(w1 + w2 + … + wi-1),可得到简化后的表格
奶牛 | 交换前 | 交换后 |
---|---|---|
i | - si | wi+1 - si |
i + 1 | wi - si+1 | - si+1 |
观察第二个表格,容易得知:
- -si < wi+1 - si
- wi - si+1 > - si+1
进一步可推导出:
- 若wi - si+1 >= wi+1 - si,则交换后两头奶牛最大风险值更小
- 若wi - si+1 < wi+1 - si,则交换前两头奶牛最大风险值更小
- 所以按照w + s从小到大顺序从高到低排列所有奶牛最优
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 51000;
struct C
{
int w, s;
bool operator < (const C t)
{
return w + s > t.w + t.s;
}
}cows[N];
int n;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)cin >> cows[i].w >> cows[i].s;
sort(cows, cows + n);
int ans = -1e9, sum = 0;
for(int i = n - 1; i >= 0; i--)ans = max(ans, sum - cows[i].s), sum += cows[i].w;
cout << ans;
return 0;
}